资讯详情

drizzle-orm 0.24.1 版本解析:onConflict 目标列修复与条件表达式 JSDoc 文档化

📅 2026/9/20 18:17:02 | 华诺云谱 👁 阅读
drizzle-orm 0.24.1 版本解析:onConflict 目标列修复与条件表达式 JSDoc 文档化
drizzle-orm 0.24.1 版本解析onConflict 目标列修复与条件表达式 JSDoc 文档化【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm导读本文围绕 drizzle-orm 0.24.1 版本的两项核心变更展开一是修复onConflict冲突目标列处理逻辑解决 upsert 场景下冲突目标识别不准确的问题二是为 SQL 条件表达式eq、ne、and、or等补齐 JSDoc 文档正式开启 drizzle-orm 的源码文档化之路。读完本文你将理解 drizzle-orm 中onConflictDoNothing/onConflictDoUpdate的完整配置方式、各数据库方言的差异以及条件表达式 JSDoc 的代码示例如何反哺开发体验。一、0.24.1 版本变更总览根据 changelogs/drizzle-orm/0.24.1.md 的官方记录该版本包含两类变更类别内容贡献者Bug 修复修复onConflict的目标target列处理PR #475wkunert文档改进为 SQL 条件表达式补充 JSDoc 文档PR #467tmcw其中 JSDoc 变更在 changelog 中被特别标注 Thanks to tmcw we have started our way to get JSDoc documentation即 drizzle-orm 从 0.24.1 开始正式将「为公开 API 编写 JSDoc」纳入项目维护流程。二、Bug 修复onConflict 目标列处理2.1 什么是 onConflict / upserton conflict是 PostgreSQL 与 SQLite 原生的「插入冲突处理」语法即通常所说的 upsert当插入的行与表中已有数据产生唯一约束/主键冲突时执行备选动作忽略或更新。drizzle-orm 将这一能力封装为链式 APIonConflictDoNothing()冲突时什么都不做放弃本次插入onConflictDoUpdate({ target, set, where })冲突时更新已有行MySQL / SingleStore 方言则对应onDuplicateKeyUpdate({ set })。0.24.1 修复的正是这些方法在目标列target解析与生成上存在的问题例如当传入多个冲突目标列、或目标列需要经过标识符转义时生成的 SQL 不够准确。2.2 SQLite 实现onConflict 目标列如何生成 SQLSQLite 侧的实现在 drizzle-orm/src/sqlite-core/query-builders/insert.ts 中。onConflictDoNothinginsert.ts#L306-L317的完整逻辑onConflictDoNothing(config: { target?: IndexColumn | IndexColumn[]; where?: SQL } {}): this { if (!this.config.onConflict) this.config.onConflict []; if (config.target undefined) { this.config.onConflict.push(sql on conflict do nothing); } else { const targetSql Array.isArray(config.target) ? sql${config.target} : sql${[config.target]}; const whereSql config.where ? sql where ${config.where} : sql; this.config.onConflict.push(sql on conflict ${targetSql} do nothing${whereSql}); } return this; }关键点不传 target时生成on conflict do nothing即对所有唯一约束冲突都静默跳过传入 target时生成on conflict (列) do nothing只针对指定列上的冲突触发target 既可以是单个IndexColumn也可以是数组源码通过Array.isArray统一包装可选where子句会被追加为where 条件实现「仅当满足某条件时才忽略冲突」的精细化控制。onConflictDoUpdateinsert.ts#L348-L366的配置更丰富它校验了where与targetWhere/setWhere不能混用onConflictDoUpdate(config: SQLiteInsertOnConflictDoUpdateConfigthis): this { if (config.where (config.targetWhere || config.setWhere)) { throw new Error( You cannot use both where and targetWhere/setWhere at the same time - where is deprecated, use targetWhere or setWhere instead., ); } // ... const whereSql config.where ? sql where ${config.where} : undefined; const targetWhereSql config.targetWhere ? sql where ${config.targetWhere} : undefined; const setWhereSql config.setWhere ? sql where ${config.setWhere} : undefined; const targetSql Array.isArray(config.target) ? sql${config.target} : sql${[config.target]}; const setSql this.dialect.buildUpdateSet(this.config.table, mapUpdateSet(this.config.table, config.set)); this.config.onConflict.push( sql on conflict ${targetSql}${targetWhereSql} do update set ${setSql}${whereSql}${setWhereSql}, ); return this; }这里对 target 的统一处理单列与多列数组归一化正是 0.24.1 修复的核心区域之一修复后多目标列的 SQL 生成保持一致。最终这些onConflict片段由方言层拼装进完整的 INSERT 语句。在 drizzle-orm/src/sqlite-core/dialect.ts#L513、dialect.ts#L587-L593 可以看到const onConflictSql onConflict?.length ? sql.join(onConflict) : undefined; // ... return sql${withSql}insert into ${table} ${insertOrder} ${valuesSql}${onConflictSql}${returningSql};即多个 onConflict 片段通过sql.join顺序拼接紧随 values 之后、returning 之前与原生 SQL 语法位置完全对应。2.3 PostgreSQL 实现target 列的标识符转义PG 侧的实现在 drizzle-orm/src/pg-core/query-builders/insert.ts。onConflictDoNothinginsert.ts#L323-L338在指定 target 时对列名做了显式转义处理targetColumn Array.isArray(config.target) ? config.target.map((it) this.dialect.escapeName(this.dialect.casing.getColumnCasing(it))).join(,) : this.dialect.escapeName(this.dialect.casing.getColumnCasing(config.target)); // ... this.config.onConflict sql(${sql.raw(targetColumn)})${whereSql} do nothing;escapeName保证列名按 PG 规则加引号转义而getColumnCasing则负责应用列命名策略snake_case 等 casing 配置这在启用自定义命名映射的项目中至关重要——这也是 onConflict 目标列容易出错的典型场景之一0.24.1 修复后该路径被统一收敛。onConflictDoUpdateinsert.ts#L369-L387同样内置了where已弃用与targetWhere/setWhere互斥的运行时校验并在参数注释中标注了targetWhere/setWhere的替代关系从类型与运行时双层约束保证 API 正确使用。2.4 MySQL / SingleStore没有 on conflict 的替代方案MySQL 与 SingleStore 并不支持on conflict语法drizzle-orm 为它们提供的是onDuplicateKeyUpdate。在 drizzle-orm/src/mysql-core/query-builders/insert.ts#L273-L279onDuplicateKeyUpdate( config: MySqlInsertOnDuplicateKeyUpdateConfigthis, ): MySqlInsertWithoutthis, TDynamic, onDuplicateKeyUpdate { const setSql this.dialect.buildUpdateSet(this.config.table, mapUpdateSet(this.config.table, config.set)); this.config.onConflict sqlupdate ${setSql}; return this as any; }由于 MySQL 没有「冲突时 do nothing」的原生写法官方 JSDocinsert.ts#L263-L271给出了一个巧妙的等价实现将任意一列设为自身值例如onDuplicateKeyUpdate({ set: { id: sqlid} })即可实现「冲突时不做任何修改」的 no-op 效果。SingleStore 侧的实现drizzle-orm/src/singlestore-core/query-builders/insert.ts#L246与之完全对称。2.5 使用示例修复后的 onConflict 实战import { sql } from drizzle-orm; // 冲突时静默忽略不指定 target任一唯一约束冲突均跳过 await db.insert(cars) .values({ id: 1, brand: BMW }) .onConflictDoNothing(); // 精确指定冲突目标列 条件 await db.insert(cars) .values({ id: 1, brand: BMW }) .onConflictDoNothing({ target: cars.id, where: sql${cars.deletedAt} is null }); // 冲突时更新多目标列数组 await db.insert(cars) .values({ id: 1, brand: BMW }) .onConflictDoUpdate({ target: [cars.id, cars.plateNo], set: { brand: Porsche }, }); // MySQL 场景无冲突则插入冲突则更新 await db.insert(cars) .values({ id: 1, brand: BMW }) .onDuplicateKeyUpdate({ set: { brand: Porsche } });三、文档改进条件表达式的 JSDoc 体系3.1 起点conditions.ts 的条件算子0.24.1 的第二个变更来自 tmcw 的 PR #467——为drizzle-orm/src/sql/expressions/conditions.ts中的条件表达式补齐 JSDoc。该文件集中定义了 drizzle-orm 最常用的过滤原语且文档风格与正文保持统一先一句功能说明再给出真实可运行的 TypeScript 示例最后用see关联相关算子。以eq为例conditions.ts#L44-L64/** * Test that two values are equal. * * Remember that the SQL standard dictates that * two NULL values are not equal, so if you want to test * whether a value is null, you may want to use * isNull instead. * * ## Examples * * ts * // Select cars made by Ford * db.select().from(cars) * .where(eq(cars.make, Ford)) * * * see isNull for a way to test equality to NULL. */ export const eq: BinaryOperator (left: SQLWrapper, right: unknown): SQL { return sql${left} ${bindIfParam(right, left)}; };这份 JSDoc 不只是形式化的注释它承担了三层职责语义澄清明确指出 SQL 标准中两个 NULL 不相等提醒用户判空应改用isNull示例即测试eq(cars.make, Ford)这样可直接粘贴运行的片段降低了 API 理解成本算子互链通过see把eq/ne/isNull/isNotNull等易混淆的算子串联成一张知识网络。同样的模式覆盖了neconditions.ts#L66-L86示例为ne(cars.make, Ford)、以及组合算子and/orconditions.ts#L88-L125、conditions.ts#L127-L143。其中and/or的文档还特别说明了「值为undefined的条件会被自动忽略」这一行为——这是 drizzle-orm 允许动态拼接过滤条件的底层保证。3.2 扩散query-builder 各方法的 JSDoc条件表达式只是起点。在 0.24.1 之后这套 JSDoc 风格快速扩散到各方言的 query-builder 中。以 SQLite 的 select builder 为例drizzle-orm/src/sqlite-core/query-builders/select.ts 中几乎所有公开方法都带有结构化的 JSDocleftJoinselect.ts#L289-L316说明 left join 对无匹配行的处理关联表列置为 null并给出带类型标注的返回示例{ user: User; pets: Pet | null; }[]unionselect.ts#L995-L1020说明去重语义并同时展示「函数式调用」与「链式调用」两种等价写法insert builder 中的onConflictDoNothing/onConflictDoUpdatedrizzle-orm/src/sqlite-core/query-builders/insert.ts#L284-L347则携带指向官方文档的See docs:链接将 IDE 内的智能提示与完整文档打通。这些 JSDoc 的实际价值在于开发者在 IDE 中悬停即可看到方法签名、语义说明与可运行的示例无需跳转文档站点同时为drizzle-kit的 introspection、类型提示测试等下游工具提供了稳定的元信息基础。3.3 对后续版本的启示从源码结构看JSDoc 工作贯穿了后续版本PG 的PgInsertOnConflictDoUpdateConfig中targetWhere/setWhere的弃用标注drizzle-orm/src/pg-core/query-builders/insert.ts#L174-L178即是该文档化进程的延续。而 gel-core 中 onConflict 方法处于注释状态drizzle-orm/src/gel-core/query-builders/insert.ts#L304-L367说明 Gel 方言的 upsert 支持仍在演进中这也是读者在选用方言时可以留意的差异点。四、结语drizzle-orm 0.24.1 是一个「小而关键」的版本onConflict目标列修复让 PostgreSQL 与 SQLite 的 upsert 在标识符转义、多列目标场景下更加可靠而条件表达式的 JSDoc 化则开启了项目长期的文档体系建设。对于使用 drizzle-orm 的开发者理解onConflictDoNothing/onConflictDoUpdate/onDuplicateKeyUpdate三套 API 的方言差异并善用 IDE 中逐步完善的 JSDoc 提示是写出健壮写入逻辑的基础。【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。