下面是目录。点击某一项可跳转到对应章节:
迁移到关系查询版本 2
npm i drizzle-orm@rc
npm i drizzle-kit@rc -D
API 更改
与 v1 相比有哪些不同
最大的更新之一在 Relations Schema 定义
第一个区别是,你不再需要在不同对象中分别为每个表单独指定 relations,然后再将它们连同 schema 一起传给 drizzle()。在 Relational Queries v2 中,你现在有一个专门的位置来为所有需要的表定义所有关系。
回调中的 r 参数提供了完整的自动补全功能——包括 schema 中的所有表,以及 one、many 和 through 等函数——本质上提供了你定义关系所需的一切。
// relations.ts
import * as schema from "./schema"
import { defineRelations } from "drizzle-orm"
export const relations = defineRelations(schema, (r) => ({
...
}));// index.ts
import { relations } from "./relations"
import { drizzle } from "drizzle-orm/mysql2"
const db = drizzle(process.env.DATABASE_URL, { relations })有什么不同?
Schema 定义
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().primaryKey(),
name: p.text(),
invitedBy: p.int('invited_by'),
});
export const posts = p.mysqlTable('posts', {
id: p.int().primaryKey(),
content: p.text(),
authorId: p.int('author_id'),
});所有关系都放在一个地方
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const usersRelation = relations(users, ({ one, many }) => ({
invitee: one(users, {
fields: [users.invitedBy],
references: [users.id],
}),
posts: many(posts),
}));
export const postsRelation = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
},
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
},
}));你仍然可以把它拆分成不同的 parts,而且这些部分的大小可以由你自由决定
import { defineRelations, defineRelationsPart } from 'drizzle-orm';
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
invitee: r.one.users({
from: r.users.invitedBy,
to: r.users.id,
}),
posts: r.many.posts(),
}
}));
export const part = defineRelationsPart(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
}
}));然后你可以把它提供给 db 实例
const db = drizzle(process.env.DB_URL, { relations: { ...relations, ...part } })无需 one 也能定义 many
在 v1 中,如果你只想要关系的 many 这一侧,你必须在另一端显式指定 one 这一侧,这带来了糟糕的开发体验。
在 v2 中,你可以直接使用 many 这一侧,无需任何额外步骤
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const usersRelation = relations(users, ({ one, many }) => ({
posts: many(posts),
}));
export const postsRelation = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
posts: r.many.posts({
from: r.users.id,
to: r.posts.authorId,
}),
},
}));新的 optional 选项
类型层面的 optional: false 会使 posts 对象中的 author 键变为必需。
当你确定这个特定实体总是存在时,应使用它。
v1 中不支持
import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
users: {
posts: r.one.posts({
from: r.users.id,
to: r.posts.authorId,
optional: false,
}),
},
}));drizzle() 中不再需要 modes
我们找到了一种适用于所有 MySQL 方言的相同策略,因此不需要再指定它们
import * as schema from './schema'
const db = drizzle(process.env.DATABASE_URL, { mode: "planetscale", schema });
// or
const db = drizzle(process.env.DATABASE_URL, { mode: "default", schema });import { relations } from './relations'
const db = drizzle(process.env.DATABASE_URL, { relations });from 和 to 的升级
我们将 fields 重命名为 from,将 references 重命名为 to,并且两者都可以接受单个值或数组
...
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
......
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
}),
......
author: r.one.users({
from: [r.posts.authorId],
to: [r.users.id],
}),
...relationName -> alias
import { relations } from "drizzle-orm/_relations";
import { users, posts } from './schema';
export const postsRelation = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
relationName: "author_post",
}),
}));import { defineRelations } from "drizzle-orm";
import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({
posts: {
author: r.one.users({
from: r.posts.authorId,
to: r.users.id,
alias: "author_post",
}),
},
}));custom types 新函数
自定义类型新增了一些函数,因此你可以在 Relational Queries v2 中控制数据的映射方式:
fromJson
可选的映射函数,用于将数据库中转换为 JSON 后返回的数据转换为期望格式
例如,当通过 RQB 或 JSON 函数查询 bigint 列时,结果字段将以字符串表示形式返回,而不是常规查询中的 bigint
为了解决这个问题,我们需要一个单独的函数来处理这类字段的映射:
fromJson(value: string): bigint {
return BigInt(value);
},这会使返回的数据从:
{
customField: "5044565289845416380";
}变为:
{
customField: 5044565289845416380n;
}forJsonSelect
可选的选择修饰函数,用于修改 JSON 函数内部列的选择方式
此类场景可能需要的额外映射可以通过 fromJson 函数处理
由 关系查询 使用
例如,当使用 bigint 时,我们需要将字段转换为 text 以保持数据完整性
forJsonSelect(identifier: SQL, sql: SQLGenerator, arrayDimensions?: number): SQL {
return sql`${identifier}::text`
},这会将查询从:
SELECT
row_to_json("t".*)
FROM
(
SELECT
"table"."custom_bigint" AS "bigint"
FROM
"table"
) AS "t"变为:
SELECT
row_to_json("t".*)
FROM
(
SELECT
"table"."custom_bigint"::text AS "bigint"
FROM
"table"
) AS "t"查询对象返回结果会从:
{
bigint: 5044565289845416000; // 由于直接转换为 JSON 格式,造成部分数据丢失
}变为:
{
bigint: "5044565289845416380"; // 由于在转换为 JSON 之前先将字段转为 text,因此数据得以保留
}const customBytes = customType<{
data: Buffer;
driverData: Buffer;
jsonData: string;
}>({
dataType: () => 'bytea',
fromJson: (value) => {
return Buffer.from(value.slice(2, value.length), 'hex');
},
forJsonSelect: (identifier, sql, arrayDimensions) =>
sql`${identifier}::text${sql.raw('[]'.repeat(arrayDimensions ?? 0))}`,
});什么是新的?
用于多对多关系的 through
以前,你需要先查询中间表,然后对每个响应手动映射出来
现在你不需要这样做了!
Schema
import * as p from 'drizzle-orm/mysql-core';
export const users = p.mysqlTable('users', {
id: p.int().primaryKey(),
name: p.text(),
verified: p.boolean().notNull(),
});
export const groups = p.mysqlTable('groups', {
id: p.int().primaryKey(),
name: p.text(),
});
export const usersToGroups = p.mysqlTable(
'users_to_groups',
{
userId: p
.int('user_id')
.notNull()
.references(() => users.id),
groupId: p
.int('group_id')
.notNull()
.references(() => groups.id),
},
(t) => [p.primaryKey({ columns: [t.userId, t.groupId] })],
);
export const usersRelations = relations(users, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const groupsRelations = relations(groups, ({ many }) => ({
usersToGroups: many(usersToGroups),
}));
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
group: one(groups, {
fields: [usersToGroups.groupId],
references: [groups.id],
}),
user: one(users, {
fields: [usersToGroups.userId],
references: [users.id],
}),
}));// Query example
const response = await db.query.users.findMany({
with: {
usersToGroups: {
columns: {},
with: {
group: true,
},
},
},
});import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema, (r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
}));// Query example
const response = await db.query.users.findMany({
with: {
groups: true,
},
});预定义过滤器
v1 中不支持
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema,
(r) => ({
groups: {
verifiedUsers: r.many.users({
from: r.groups.id.through(r.usersToGroups.groupId),
to: r.users.id.through(r.usersToGroups.userId),
where: {
verified: true,
},
}),
},
})
);// 查询示例:获取所有已验证用户所在的组
const response = await db.query.groups.findMany({
with: {
verifiedUsers: true,
},
});where 现在是对象
const response = db._query.users.findMany({
where: (users, { eq }) => eq(users.id, 1),
});const response = db.query.users.findMany({
where: {
id: 1,
},
});完整 API 参考请查看我们的 Select Filters 文档
使用 RAW 的复杂过滤示例
// schema.ts
import { int, json, mysqlTable, text, timestamp } from 'drizzle-orm/mysql-core';
export const users = mysqlTable('users', {
id: int().primaryKey(),
name: text(),
email: text().notNull(),
age: int(),
createdAt: timestamp('created_at').defaultNow(),
lastLogin: timestamp('last_login'),
subscriptionEnd: timestamp('subscription_end'),
lastActivity: timestamp('last_activity'),
preferences: json(), // 用户设置/偏好 的 JSON 列
interests: json().$type<string[]>(), // 用户兴趣的数组列
});const response = await db.query.users.findMany({
where: {
AND: [
{
OR: [{ RAW: (table) => sql`${table.name} LIKE 'john%'` }, { name: { like: "jane%" } }],
},
{
RAW: (table) => sql`${table.age} BETWEEN 25 AND 35`
},
],
},
}),orderBy 现在是对象
const response = db._query.users.findMany({
orderBy: (users, { asc }) => [asc(users.id)],
});const response = db.query.users.findMany({
orderBy: { id: "asc" },
});通过关系进行过滤
v1 中不支持
示例:获取所有 ID > 10 且至少有一篇内容以 “M” 开头的帖子
const usersWithPosts = await db.query.usersTable.findMany({
where: {
id: {
gt: 10
},
posts: {
content: {
like: 'M%'
}
}
},
});在关联对象上使用 offset
v1 中不支持
await db.query.posts.findMany({
limit: 5,
offset: 2, // 正确 ✅
with: {
comments: {
offset: 3, // 正确 ✅
limit: 3,
},
},
});如何将 relations schema 定义从 v1 迁移到 v2
方案 1:使用 drizzle-kit pull
在新版本中,drizzle-kit pull 支持以新语法拉取 relations.ts 文件:
第 1 步
npx drizzle-kit pull
第 2 步
将生成的 relations 代码从 drizzle/relations.ts 转移到你用于定义 relations 的文件中
├ 📂 drizzle
│ ├ 📂 meta
│ ├ 📜 migration.sql
│ ├ 📜 relations.ts ────────┐
│ └ 📜 schema.ts |
├ 📂 src │
│ ├ 📂 db │
│ │ ├ 📜 relations.ts <─────┘
│ │ └ 📜 schema.ts
│ └ 📜 index.ts
└ …drizzle/relations.ts 包含对 drizzle/schema.ts 中所有表的导入,类似这样:
import * as schema from './schema'你可能需要将此导入改为所有 schema 表所在的文件。
如果有多个 schema 文件,你可以这样做:
import * as schema1 from './schema1'
import * as schema2 from './schema2'
...第 3 步
修改 drizzle 数据库实例创建,并提供 relations 对象而不是 schema
import * as schema from './schema'
import { drizzle } from 'drizzle-orm/mysql2'
const db = drizzle('<url>', { schema })// 应从第 2 步中的文件导入
import { relations } from './relations'
import { drizzle } from 'drizzle-orm/mysql2'
const db = drizzle('<url>', { relations })如果你使用的是 MySQL 方言,只要 v2 中不再需要 mode,就可以将其从 drizzle() 中移除
方案 2:手动迁移
如果你想手动迁移,可以查看我们的 Drizzle Relations 部分 获取完整 API 参考,以及一对一、一对多和多对多关系的示例。
如何将查询从 v1 迁移到 v2
迁移 where 语句
你可以查看我们的 Select Filters 文档 来查看示例和完整 API 参考。
使用新语法后,你可以使用 AND、OR、NOT 和 RAW,以及之前在 Relations v1 中可用的所有过滤运算符。
示例
const response = await db.query.users.findMany({
where: {
age: 15,
},
});SELECT
`d0`.`id` AS `id`,
`d0`.`name` AS `name`,
`d0`.`age` AS `age`
FROM
`users` AS `d0`
WHERE
(`d0`.`age` = 15);迁移 orderBy 语句
排序被简化为单个对象,你只需指定列和排序方向(asc 或 desc)
const response = db._query.users.findMany({
orderBy: (users, { asc }) => [asc(users.id)],
});const response = db.query.users.findMany({
orderBy: { id: "asc" },
});迁移 many-to-many 查询
Relational Queries v1 管理多对多查询的方式非常复杂。 你必须显式通过中间表进行查询,然后将这些表映射出来,就像这样:
const response = await db.query.users.findMany({
with: {
usersToGroups: {
columns: {},
with: {
group: true,
},
},
},
});升级到 Relational Queries v2 后,你的多对多关系将如下所示:
import * as schema from './schema';
import { defineRelations } from 'drizzle-orm';
export const relations = defineRelations(schema, (r) => ({
users: {
groups: r.many.groups({
from: r.users.id.through(r.usersToGroups.userId),
to: r.groups.id.through(r.usersToGroups.groupId),
}),
},
groups: {
participants: r.many.users(),
},
}));当你迁移查询后,它会变成这样:
// Query example
const response = await db.query.users.findMany({
with: {
groups: true,
},
});内部更改
- 每个
drizzle数据库、session、migrator和transaction实例都更新了用于 RQB v2 查询的新泛型参数
示例
migrator
export async function migrate<
TSchema extends Record<string, unknown>
>(
db: MySql2Database<TSchema>,
config: MigrationConfig,
) {
...
}export async function migrate<TRelations extends AnyRelations>(
db: MySql2Database<TRelations>,
config: MigrationConfig,
) {
...
}session
export class MySql2Session<
TFullSchema extends Record<string, unknown>,
TSchema extends V1.TablesRelationalConfig,
> extends MySqlSession<MySqlQueryResultHKT, MySql2PreparedQueryHKT, TFullSchema, TSchema>export class MySql2Session<
TRelations extends AnyRelations,
> extends MySqlSession<MySqlQueryResultHKT, TRelations>transaction
export class MySql2Transaction<
TFullSchema extends Record<string, unknown>,
TSchema extends V1.TablesRelationalConfig,
> extends MySqlTransaction<MySql2QueryResultHKT, MySql2PreparedQueryHKT, TFullSchema, TSchema>export class MySql2Transaction<
TRelations extends AnyRelations,
> extends MySqlTransaction<MySql2QueryResultHKT, TRelations>driver
export class MySql2Database<
TSchema extends Record<string, unknown> = Record<string, never>,
> extends MySqlDatabase<MySql2QueryResultHKT, MySql2PreparedQueryHKT, TSchema>export class MySql2Database<
TRelations extends AnyRelations = EmptyRelations,
> extends MySqlDatabase<MySql2QueryResultHKT, TRelations>- 更新了
DrizzleConfig的泛型,新增TRelations参数和relations: TRelations字段
示例
export interface DrizzleConfig<
TSchema extends Record<string, unknown> = Record<string, never>
> {
logger?: boolean | Logger;
schema?: TSchema;
casing?: Casing;
}export interface DrizzleConfig<
TRelationConfigs extends AnyRelations = EmptyRelations,
> {
logger?: boolean | Logger | undefined;
relations?: TRelationConfigs | undefined;
cache?: Cache | undefined;
jit?: boolean | undefined;
}- 以下实体已从
drizzle-orm和drizzle-orm/relations中移除。原始导入现在包含了 Relational Queries v2 使用的新类型,因此如果你打算使用旧类型,请务必更新导入:
所有已移除实体列表
RelationsTableRelationsKeysOnlyExtractTableRelationsFromSchemaExtractRelationsFromTableExtraConfigSchemagetOperatorsFindTableByDBNameRelationalSchemaConfigRelationConfigextractTablesRelationalConfigrelationscreateOnecreateManyNormalizedRelationnormalizeRelationcreateTableRelationsHelpersTableRelationsHelpers
- 同样地,
${dialect}-core/query-builders/query文件已更新为使用 RQB v2 的替代项
示例
import { RelationalQueryBuilder, MySqlRelationalQuery } from 'drizzle-orm/mysql-core/query-builders/query';