如何在Mongoose Schema验证器中获取Mongoose事务Session
Mongoose关联引用校验中访问事务会话的实现方案
问题根因
- 示例代码中使用箭头函数定义自定义验证器,由于箭头函数不绑定自身的
this,会继承外层词法作用域的上下文,当前场景下外层为模块顶层严格作用域,因此最终输出的this为undefined,完全丢失了Mongoose提供的文档实例上下文。 - 按照Mongoose的内置机制,字段验证器的上下文默认指向当前正在校验的文档实例,只有使用普通函数(非箭头函数)定义验证器时,
this才能正确拿到文档实例上挂载的会话访问方法。
正确实现流程
- 将字段的自定义验证器改写为普通异步函数,此时函数内部的
this会自动绑定当前待校验的文档实例。 - 在验证器内部直接调用
this.$session()方法,即可获取当前文档关联的Mongoose会话实例。 - 执行关联字段的存在性校验时,将拿到的会话绑定到查询链上,确保所有校验操作与文档写入处于同一个事务上下文内。
修正后的可运行示例代码
import mongoose from "mongoose"; async function run() { // 被关联的Account模型 const accountSchema = new mongoose.Schema({ name: { type: String, required: true } }); const Account = mongoose.model("Account", accountSchema); const userSchema = new mongoose.Schema({ accountId: { type: mongoose.Schema.Types.ObjectId, required: true, // 使用普通函数定义验证器,确保this正确绑定当前文档 validate: async function (val) { // 通过文档实例获取关联的会话 const currentSession = this.$session(); // 基于会话执行关联对象存在性校验 const validCount = await Account .findById(val) .session(currentSession) .countDocuments(); // 校验不通过时抛出错误 if (validCount === 0) { throw new Error("关联的账户对象不存在"); } } } }); const User = mongoose.model("User", userSchema); await mongoose.connect("mongodb://localhost:27017/mongoose_demo"); // 事务操作示例 const session = await mongoose.startSession(); session.startTransaction(); try { // 在同一事务内创建被关联的账户对象 const [createdAccount] = await Account.create( [{ name: "测试业务账户" }], { session } ); // 创建用户文档时绑定当前会话,保存时触发的验证器可正常获取会话 const testUser = new User({ accountId: createdAccount._id }); await testUser.save({ session }); await session.commitTransaction(); } catch (err) { await session.abortTransaction(); throw err; } finally { session.endSession(); } } run();
常见注意事项
- 禁止使用箭头函数定义字段级自定义验证器,箭头函数的词法绑定机制会直接切断Mongoose的上下文注入逻辑,导致永远无法获取文档实例与会话信息。
- 文档在初始化、保存操作时必须显式传入绑定的会话,否则
this.$session()会返回空值,后续的校验查询会脱离事务隔离规则,出现校验逻辑与实际写入数据不一致的问题。 - 所有关联校验的查询操作都需要通过
.session(会话实例)的方法绑定上下文,否则查询不会读取当前事务内未提交的写入数据,导致明明已经在事务内创建的关联对象,校验时却提示不存在。
内容的提问来源于stack exchange,提问作者yinonby
相关产品推荐
相关产品推荐

