
数据库后端【免费下载链接】objection.jsAn SQL-friendly ORM for Node.js项目地址https://gitcode.com/gh_mirrors/ob/objection.js点击查看免费下载本篇技术指南基于 objection.js 官方 迁移文档 编写系统梳理从 objection 1.x → 2.0 → 3.0 两个大版本升级中引入的全部破坏性变更breaking changes并结合当前仓库objection 3.1.5的源码与集成测试说明每项变更的成因、影响范围以及可操作的迁移步骤。读完本文你将能对照自己的项目逐条排查Node/knex 版本约束、TypeScript 类型收窄、PromiseLike语义、被移除的废弃方法、#ref安全策略、relate/$relatedQuery返回值变化、db-errors 错误包装等从而以最小的代价平滑升级到 objection 3.x。迁移路线总览objection.js 的版本演进中有两条关键的迁移路径1.x → 2.0以 API 清理为主重写了 TypeScript 类型移除了 Bluebird 与 lodash 依赖并引入 db-errors 错误包装2.0 → 3.0继续收紧运行环境要求修正 TypeScript 类型语义并彻底删除所有在 2.0 中被标记废弃的方法。当前仓库的 package.json 显示版本为3.1.5engines声明node 14.0.0peerDependencies要求knex 1.0.1详见下文“版本要求已进一步提高”一节。这说明 3.x 的实际运行门槛比迁移文档写作时更高升级前务必核对你的运行时环境。以下各节按“2.x → 3.0 → 1.x → 2.0”的顺序依次讲解先从离你最近、最紧迫的 3.0 破坏性变更开始。第一部分objection 2.x → 3.0 迁移3.0 的破坏性变更集中于运行环境下限提升、TypeScript 类型修正与废弃 API 清除三类多数情况下编译器的报错信息就能指引你完成迁移。不再支持 Node 12objection 3.0 要求至少 Node 12 才能运行。迁移文档原文如此规定而当前仓库的 package.json 进一步将engines收紧为node 14.0.0。因此在实际升级前请先确认你的部署环境与 CI 流水线使用的 Node 版本低于 14 的运行时将无法通过 npm 安装或启动。不再支持 knex 0.95objection 3.0 要求至少 knex 0.95.0原因是 knex 在 0.95.0 中引入了破坏性变更。当前仓库的 package.json 将 peerDependencies 进一步提高为knex 1.0.1而仓库自身的开发依赖使用的是 knex ^3.1.0。升级 objection 时请同步升级 knex并阅读 knex 0.95.0 与 1.x 的发布说明排查查询构建层面的兼容问题。TypeScriptfindById/findOne的返回值类型被修正在 2.0 中findById、findOne、first等“返回单条记录或空值”的方法被错误地标注为总是返回值。3.0 将其修正为SomeModel | undefined。查看 typings/objection/index.d.ts 中的相关定义可以印证这一点findById(id: MaybeCompositeId): MaybeSingleQueryBuilderthis; findByIds(ids: MaybeCompositeId[]): this; findOne: WhereMethodMaybeSingleQueryBuilderthis;其中MaybeSingleQueryBuilder最终解析为 QueryBuilderM, M | undefined即查询结果可能为undefined。这一改动会让“此前信任旧类型”的代码产生编译错误你需要显式收窄类型。迁移方式一手动判空升级前编译可通过但运行时可能因访问undefined.id而崩溃const person await Person.query().findById(id) console.log(person.id)升级后先判空再使用const person await Person.query().findById(id) if (!person) { throw new Error(Person not found) } console.log(person.id)迁移方式二链式调用throwIfNotFoundthrowIfNotFound会在结果为空时抛出错误并把返回类型从M | undefined收窄回M这是最简洁的写法const person await Person.query().findById(id).throwIfNotFound() console.log(person.id)仓库集成测试 tests/integration/find.js 覆盖了.throwIfNotFound()的空结果抛错、非空结果放行、自定义错误消息以及createNotFoundError钩子等场景可作为行为参考。该方法还能配合自定义消息const person await Person.query().findById(id).throwIfNotFound({ message: customMessage })TypeScriptQueryBuilder由继承Promise改为实现PromiseLike2.0 及更早版本在类型层面让QueryBuilder继承Promise因此下面的代码可以编译function findPerson(id: number): PromisePerson { return Person.query().findById(id); }但运行时QueryBuilder并非真正的Promise它只是一个“thenable”可 await 对象。3.0 改为使用 TypeScript 为 thenable 设计的PromiseLike类型。查看 typings/objection/index.d.ts 的定义export class QueryBuilderM extends Model, R M[] implements CatchablePromiseLikeR {CatchablePromiseLike继承自PromiseLike见 typings/objection/index.d.ts并额外提供了catch方法。这一改动会让“把查询构建器直接当作Promise返回”的函数产生编译错误有三种修复方式方式一链式调用execute()得到真正的 Promisefunction findPerson(id: number): PromisePerson { return Person.query().findById(id).execute(); }方式二函数返回类型改为PromiseLikefunction findPerson(id: number): PromiseLikePerson { return Person.query().findById(id); }方式三将函数声明为asyncasync function findPerson(id: number): PromisePerson { return Person.query().findById(id); }三种方式语义等价按团队代码风格选择其一即可。移除了一批废弃方法与特性2.0 中所有被标记废弃运行时打印废弃警告的方法在 3.0 中已全部删除。仓库中遗留的deprecate工具实现lib/utils/deprecate.js可以帮你理解 2.0 时代的警告机制它通过LOGGED_DEPRECATIONS集合保证同一条警告每个进程只打印一次并调用console.warn输出。也就是说2.x 时期你看到的“每个进程只出现一次”的警告就是在提示这些将在 3.0 中被删除的 API。升级到 3.0 之前请在你的代码库中全局搜索这些废弃调用的残余逐项替换为警告信息中提示的新方法。典型例子是 2.0 中已废弃的Model.relatedFindQueryMutates与Model.relatedInsertQueryMutates详见第二部分“$relatedQuery不再修改实例”一节。第二部分objection 1.x → 2.0 迁移2.0 带来了大量新特性但重心在于 API 清理由此产生了一批破坏性变更。除下列条目外2.0 还废弃并替换了大量方法——旧方法仍可使用但每次进程会打印一次警告警告内容会告诉你应该改用哪个新方法你可以按警告逐项替换。Node 6 和 7 不再受支持objection 2.0 要求至少 Node 8 才能运行Node 6、7 停止支持。结合第一部分的说明3.0 进一步要求 Node ≥ 12当前仓库 package.json 实际要求 ≥ 14请一并规划 Node 版本的升级节奏。modify方法签名变更旧版允许通过多个参数指定多个 modifier 名称Person.query().modify(foo, bar);2.0 起modify只使用第一个参数指定 modifiers其余参数全部作为 modifier 的入参。如果确实需要一次应用多个 modifier请把它们包进数组Person.query().modify([foo, bar]);这是本次迁移中最容易踩坑的签名变更之一请检查所有使用多参数modify的调用点避免修饰器入参被误当成 modifier 名称。Bluebird 与 lodash 被移除2.0 之前objection 的所有异步操作返回 Bluebird Promise2.0 起改为使用原生Promise。相应地以下 Bluebird 专属方法从QueryBuilder上移除mapreducereflectbindspreadasCallbacknodeify你需要全库排查确保没有使用上述 Bluebird 方法也不要假设 objection 返回的是 Bluebird Promise。此外以下导出已被删除import { Promise, lodash } from objection;2.0 起 objection 不再导出Promise和lodash属性请改用原生Promise与独立的 lodash 包。数据库错误统一来自 db-errors 库2.0 之前数据库操作失败时 objection 会直接透传数据库客户端抛出的原生错误2.0 起错误被 db-errors 库包装而当前仓库的 package.json 中db-errors: ^0.2.3依然作为直接依赖存在说明这一机制延续到了 3.x。db-errors 的包装错误暴露了nativeError属性。如果你依赖旧错误的属性例如err.code最快的迁移办法是try { await Person.query().where(foo, bar) } catch (err) { if (err.code 13514) { ... } }改为try { await Person.query().where(foo, bar) } catch (err) { err err.nativeError || err if (err.code 13514) { ... } }更推荐的做法是使用 db-errors 提供的具体错误类如UniqueViolationError、NotNullViolationError等按错误类型而不是错误码处理参见仓库的 错误处理配方。insertGraph/upsertGraph中的#ref引用必须显式开启allowRefs: true从 2.0 开始在insertGraph与upsertGraph中使用#ref: someId或#ref{someId.someProp}引用必须显式传入allowRefs: true选项await Person.query().insertGraph(graphWithRefs, { allowRefs: true });为什么这样做这是出于安全考虑。攻击者理论上可以利用#ref{someId.someProperty}引用访问对象中敏感字段例如窃取用户密码哈希const graphUpsertSentByTheAttacker { user: { id: 13431, #id: user, }, movie: { name: #ref{user.passwordHash}, }, };随后攻击者就能从 movie 的 name 字段中取出密码哈希。需要澄清的是要让该攻击成立攻击者必须先能访问修改用户信息的 API更重要的是引用只能访问对象本身里的属性永远无法访问数据库中的列——只有当程序在调用upsertGraph之前把哈希写进 graph 对象时上述 graph以及其它可构想的 graph才会把密码哈希泄露到 movie.name 中。这属于极低概率场景且就密码场景而言还要求攻击者能访问修改用户密码的路由。结论尽管当前能实际发起的攻击可能性很小官方仍建议永远不要对未经校验的用户输入使用upsertGraph(graph, { allowRefs: true })。从源码看该选项的解析位于 lib/queryBuilder/graph/GraphOptions.js而 lib/queryBuilder/graph/GraphUpsert.js 会在未开启allowRefs时抛出错误#ref references are not allowed in a graph by default. see the allowRefs insert/upsert graph option。集成测试 tests/integration/insertGraph.js 验证了未开启时使用#ref与#ref{}都会抛错、开启后正常工作的行为针对该安全问题还专门有回归测试 tests/integration/misc/refAttack.js。relate方法现在始终返回受影响行数1.x 中relate在ManyToManyRelation场景返回插入的中间表行在其它关系场景返回更新的行数。2.0 起统一返回表示受影响行数的整数。请检查代码中所有对relate返回值的使用方式并相应调整。$relatedQuery不再修改实例在 objection 1.x 中执行下面的代码会在somePerson上新增pets属性并保存结果await somePerson.$relatedQuery(pets);2.0 起这一行为不再发生同理以下插入操作也不会再把新宠物追加到somePerson.pets数组await somePerson.$relatedQuery(pets).insert(pet);替代方案需要填充关系时使用withGraphFetched与fetchGraph方法或手动赋值somePerson.pets await somePerson.$relatedQuery(pets);若想恢复 1.x 行为可通过静态属性Model.relatedInsertQueryMutates与Model.relatedFindQueryMutates切换但注意它们在 2.0 中已标记废弃且已在 3.0 中被移除。从源码看这两个静态属性在 lib/model/Model.js 中默认被设为false对应的读取访问器位于 lib/model/Model.js。升级到 3.0 时请删除任何对这两个属性的依赖。context()现在等价于mergeContext()1.x 中QueryBuilder的context方法会用传入对象替换当前上下文2.0 起改为合并。如需旧行为请先调用clearContext清空上下文builder.clearContext().context(newObject);Model.raw与Model.fn现在返回 objection 的 raw 与 fn1.x 中Model.raw返回 knex 的 raw builderModel.fn返回 knex 的FunctionHelper实例2.0 起它们改为返回 objection 自己的 raw 与 fn 辅助工具。需要 knex 原生 raw builder 时请直接使用knex.raw。QueryBuilder.toString与QueryBuilder.toSql被移除这两个方法在 2.0 中被移除替代写法是builder.toKnexQuery().toSQL()从源码看lib/queryBuilder/QueryBuilderOperationSupport.js 中保留了toKnexQuery将 objection 查询构建器转换为 knex 查询构建器并基于它提供了toString()返回 SQL 字符串。也就是说生成 SQL 的推荐路径始终是先toKnexQuery()再调用 knex 的toSQL()。TypeScript 类型被完全重写2.0 对类型定义做了彻底重写大量类型名称发生了变化。如果你只依赖类型推断默认不会遇到太多错误但凡是显式声明过 objection 类型的地方除Model外都可能需要调整。无法给出通用的迁移步骤因为迁移量取决于你在多大程度上信任类型推断、又在多大程度上使用了显式类型。但有几点值得注意QueryBuilder不再接受三个泛型参数而是两个M, R关系属性不应再声明为PartialModel或PartialModel[]直接使用Model与Model[]即可insertGraph、upsertGraph等方法会正常工作。当前仓库的 typings/objection/index.d.ts 即为 3.x 重写后的类型定义可作为升级后的对照基准仓库自带的 TypeScript 测试如 tests/ts/query-builder-api/find-methods.ts、tests/ts/query-builder-api/mutating-methods.ts与npm run test:typings内部执行tsc见 package.json可用于验证你的类型迁移结果。迁移清单与常见问题将本文要点整理为一份可勾选的升级清单运行环境Node 升级到 ≥ 12当前仓库要求 ≥ 14knex 升级到 ≥ 0.95当前仓库要求 ≥ 1.0.1类型收窄所有findById/findOne/first的结果先判空或链式调用.throwIfNotFound()Promise 语义凡把查询构建器直接返回为PromiseT的函数改为.execute()、PromiseLikeT或async函数废弃 API清理relatedFindQueryMutates/relatedInsertQueryMutates及其它 2.x 时代打印过废弃警告的方法modify签名多 modifier 改为数组传参其余参数视为 modifier 入参错误处理数据库错误改为通过err.nativeError取原生错误或改用 db-errors 错误类#ref安全insertGraph/upsertGraph使用引用时显式传{ allowRefs: true }并避免对未校验的用户输入开启返回值语义relate统一返回受影响行数$relatedQuery不再写回实例改用withGraphFetched/fetchGraph或手动赋值上下文与工具方法context()改为合并语义必要时先clearContext()Model.raw/Model.fn语义变更SQL 生成改用toKnexQuery().toSQL()类型定义QueryBuilder泛型参数由三个改为两个关系属性直接用Model/Model[]。常见问题速览为什么console.log(person.id)在 3.0 编译报错因为findById现在返回M | undefined需要判空或throwIfNotFound()。为什么把 query builder 当作Promise返回会报错因为类型已从继承Promise改为实现PromiseLike两者在类型系统中不可直接互赋。升级后数据库错误码怎么取用err.nativeError || err拿到原生错误或改用 db-errors 的错误类判断。#ref为什么默认禁止防止攻击者通过引用读取对象中的敏感字段官方建议永远不要对未校验输入开启allowRefs。最后提醒本文所有迁移步骤均以当前仓库objection 3.1.5的源码与测试为准若你在迁移中遇到文档未覆盖的缺口可在项目中结合 源码、类型定义 与 集成测试 自行验证并参考 发布说明 了解各版本细节。赞分享数据库后端【免费下载链接】objection.jsAn SQL-friendly ORM for Node.js项目地址https://gitcode.com/gh_mirrors/ob/objection.js点击查看免费下载相关推荐Luxon 升级指南从 1.x / 2.x 迁移到 3.0 的破坏性变更全解析Luxon 升级指南从 1.x / 2.x 迁移到 3.0 的破坏性变更全解析 Luxon 是专为 JavaScript 设计的日期与时间处理库提供了 Da开发工具XGBoost R 包迁移指南从 1.x 到 2.x 的破坏性 API 变更详解XGBoost R 包迁移指南从 1.x 到 2.x 的破坏性 API 变更详解 导读 XGBoost 的 R 语言绑定在 1.x 与 2.x 之间经历了一次人工智能机器学习psutil 2.0 API 迁移指南从 1.x 升级到 2.x 的破坏性变更与移植策略psutil 2.0 API 迁移指南从 1.x 升级到 2.x 的破坏性变更与移植策略 导读 psutil 是一个跨平台的进程与系统监控库其 2.0 版本可观测性系统编程上一篇炉石传说脚本终极指南5分钟快速上手开源自动化工具下一篇免费压缩包密码恢复工具让遗忘的密码不再成为障碍创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考