DrizzleORM v0.29.0 发布
Drizzle ORM version
0.29.0will require a minimum Drizzle Kit version of0.20.0, and vice versa. Therefore, when upgrading to a newer Drizzle ORM version, you will also need to upgrade Drizzle Kit. This may result in some breaking changes between versions, especially if you need to upgrade Drizzle Kit while your Drizzle ORM version is earlier than<0.28.0>.
新功能
🎉 MySQL unsigned 选项用于 bigint
现在您可以指定 bigint unsigned 类型。
const table = mysqlTable('table', {
id: bigint('id', { mode: 'number', unsigned: true }),
});在 文档 中阅读更多内容
🎉 改进的查询构建器类型
从 0.29.0 开始,默认情况下,由于 Drizzle 中所有查询构建器尽量遵循 SQL,您只能调用大多数方法一次。例如,在 SELECT 语句中可能只有一个 WHERE 子句,因此您只能调用 .where() 一次:
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // ❌ 类型错误 - where() 只能调用一次这种行为对于常规的查询构建很有用,即当您一次创建整个查询时。然而,当您想动态构建查询时,这就成了一个问题,即如果您有一个共享函数,该函数接受一个查询构建器并增强它。为了解决这个问题,Drizzle 为查询构建器提供了一种特殊的“动态”模式,移除了仅调用方法一次的限制。要启用它,您需要在查询构建器上调用 .$dynamic()。
让我们看看如何通过实现一个简单的 withPagination 函数来工作,该函数根据提供的页码和可选的页面大小向查询添加 LIMIT 和 OFFSET 子句:
function withPagination<T extends PgSelect>(
qb: T,
page: number,
pageSize: number = 10,
) {
return qb.limit(pageSize).offset(page * pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // ❌ 类型错误 - 查询构建器不在动态模式
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // ✅ OK请注意 withPagination 函数是泛型的,这允许您在其中修改查询构建器的结果类型,例如通过添加联接:
function withFriends<T extends PgSelect>(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);详细信息请参阅 文档。
🎉 指定主键和外键名称的可能性
当约束名称超过数据库的 64 个字符限制时,会出现问题。这会导致数据库引擎截断名称,从而可能导致问题。从 0.29.0 开始,您可以为 primaryKey() 和 foreignKey() 指定自定义名称。我们还弃用了旧的 primaryKey() 语法,仍然可以使用,但将在未来的版本中移除。
const table = pgTable('table', {
id: integer('id'),
name: text('name'),
}, (table) => ({
cpk: primaryKey({ name: 'composite_key', columns: [table.id, table.name] }),
cfk: foreignKey({
name: 'fkName',
columns: [table.id],
foreignColumns: [table.name],
}),
}));详细信息请参阅 文档。
🎉 读取副本支持
现在您可以使用 Drizzle 的 withReplica 函数为读取副本和进行写操作的主实例指定不同的数据库连接。默认情况下,withReplicas 将在读取操作中使用随机的读取副本,在所有其他数据修改操作中使用主实例。您还可以指定自定义逻辑以选择使用哪个读取副本连接。您可以自由做出任何加权的自定义决策。以下是一些用法示例:
const primaryDb = drizzle({ client });
const read1 = drizzle({ client });
const read2 = drizzle({ client });
const db = withReplicas(primaryDb, [read1, read2]);
// 从主数据库读取
db.$primary.select().from(usersTable);
// 从 read1 或 read2 连接读取
db.select().from(usersTable)
// 使用主数据库进行删除操作
db.delete(usersTable).where(eq(usersTable.id, 1))选择读取副本的自定义逻辑的实现示例,其中第一个副本有 70% 的机会被选择,第二个副本有 30% 的机会被选择。请注意,您可以实现任何类型的随机选择以选择读取副本。
const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
const weight = [0.7, 0.3];
let cumulativeProbability = 0;
const rand = Math.random();
for (const [i, replica] of replicas.entries()) {
cumulativeProbability += weight[i]!;
if (rand < cumulativeProbability) return replica;
}
return replicas[0]!;
});withReplicas 函数在 Drizzle ORM 的所有方言中可用。
详细信息请参阅 文档。
🎉 支持集合运算符 (UNION, UNION ALL, INTERSECT, INTERSECT ALL, EXCEPT, EXCEPT ALL)
特别感谢 @Angelelz 进行的重大贡献,从 API 讨论到正确的类型检查和运行时逻辑,以及一组全面的测试。这极大地帮助我们在此版本中交付此功能。
用法示例:
所有集合运算符可以通过两种方式使用:导入方式 或 构建器方式。
// 导入方式
import { union } from 'drizzle-orm/pg-core'
const allUsersQuery = db.select().from(users);
const allCustomersQuery = db.select().from(customers);
const result = await union(allUsersQuery, allCustomersQuery);// 构建器方式
const result = await db.select().from(users).union(db.select().from(customers));详细信息请参阅 文档。
🎉 新的 MySQL 代理驱动
已发布新的驱动程序,允许您使用 MySQL 数据库创建自己的 HTTP 驱动程序实现。您可以在 ./examples/mysql-proxy 文件夹中找到用法示例。
您需要在服务器上实现两个端点,这些端点将用于查询和迁移(迁移端点是可选的,仅在您希望使用 Drizzle 迁移时)。服务器和驱动程序的实现由您决定,因此您没有任何限制。您可以添加自定义映射、日志记录等。
您可以在 ./examples/mysql-proxy 文件夹中找到服务器和驱动程序的实现示例。
// 驱动
import axios from 'axios';
import { eq } from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/mysql-proxy';
import { migrate } from 'drizzle-orm/mysql-proxy/migrator';
import { cities, users } from './schema';
async function main() {
const db = drizzle(async (sql, params, method) => {
try {
const rows = await axios.post(`${process.env.REMOTE_DRIVER}/query`, {
sql,
params,
method,
});
return { rows: rows.data };
} catch (e: any) {
console.error('来自 pg 代理服务器的错误:', e.response.data);
return { rows: [] };
}
});
await migrate(db, async (queries) => {
try {
await axios.post(`${process.env.REMOTE_DRIVER}/migrate`, { queries });
} catch (e) {
console.log(e);
throw new Error('代理服务器无法进行迁移');
}
}, { migrationsFolder: 'drizzle' });
await db.insert(cities).values({ id: 1, name: 'name' });
await db.insert(users).values({
id: 1,
name: 'name',
email: 'email',
cityId: 1,
});
const usersToCityResponse = await db.select().from(users).leftJoin(
cities,
eq(users.cityId, cities.id),
);
}在 文档 中阅读更多内容
🎉 新的 PostgreSQL 代理驱动
与 MySQL 一样,您现在可以为 PostgreSQL 数据库实现自己的 http 驱动程序。您可以在 ./examples/pg-proxy 文件夹中找到用法示例。
您需要在服务器上实现两个端点,这些端点将用于查询和迁移(迁移端点是可选的,仅在您希望使用 Drizzle 迁移时)。服务器和驱动程序的实现由您决定,因此您没有任何限制。您可以添加自定义映射、日志记录等。
您可以在 ./examples/pg-proxy 文件夹中找到服务器和驱动程序的实现示例。
import axios from 'axios';
import { eq } from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/pg-proxy';
import { migrate } from 'drizzle-orm/pg-proxy/migrator';
import { cities, users } from './schema';
async function main() {
const db = drizzle(async (sql, params, method) => {
try {
const rows = await axios.post(`${process.env.REMOTE_DRIVER}/query`, { sql, params, method });
return { rows: rows.data };
} catch (e: any) {
console.error('来自 pg 代理服务器的错误:', e.response.data);
return { rows: [] };
}
});
await migrate(db, async (queries) => {
try {
await axios.post(`${process.env.REMOTE_DRIVER}/query`, { queries });
} catch (e) {
console.log(e);
throw new Error('代理服务器无法进行迁移');
}
}, { migrationsFolder: 'drizzle' });
const insertedCity = await db.insert(cities).values({ id: 1, name: 'name' }).returning();
const insertedUser = await db.insert(users).values({ id: 1, name: 'name', email: 'email', cityId: 1 });
const usersToCityResponse = await db.select().from(users).leftJoin(cities, eq(users.cityId, cities.id));
}详细信息请参阅 文档。
🎉 D1 批处理 API 支持
参考: https://developers.cloudflare.com/d1/platform/client-api/#dbbatch
批处理 API 用法示例:
const batchResponse = await db.batch([
db.insert(usersTable).values({ id: 1, name: 'John' }).returning({
id: usersTable.id,
}),
db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)),
db.query.usersTable.findMany({}),
db.select().from(usersTable).where(eq(usersTable.id, 1)),
db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(
usersTable,
),
]);type BatchResponse = [
{
id: number;
}[],
D1Result,
{
id: number;
name: string;
verified: number;
invitedBy: number | null;
}[],
{
id: number;
name: string;
verified: number;
invitedBy: number | null;
}[],
{
id: number;
invitedBy: number | null;
}[],
];所有可以在 db.batch 内部使用的构建器:
`db.all()`,
`db.get()`,
`db.values()`,
`db.run()`,
`db.query.<table>.findMany()`,
`db.query.<table>.findFirst()`,
`db.select()...`,
`db.update()...`,
`db.delete()...`,
`db.insert()...`,更多用法示例请见: integration-tests/tests/d1-batch.test.ts 和 文档。
Drizzle Kit 0.20.0
- 使用
defineConfig函数定义 drizzle.config 的新方式 - 使用 wrangler.toml 文件通过 Drizzle Studio 访问 Cloudflare D1 的可能性
- Drizzle Studio 正在迁移至 https://local.drizzle.studio/
bigint unsigned支持primaryKeys和foreignKeys现在可以具有自定义名称- 环境变量现在自动获取
- 一些 bug 修复和改进
您可以在 这里 阅读有关 drizzle-kit 更新的更多信息。