2022-02-13 06:44:44
答案:在 MySQL 和 Sequelize 中,数据库事务处理是确保数据一致性的关键机制。Sequelize 提供了两种事务处理方案:非托管式(Unmanaged)和托管式(Managed)。以下是具体实现及优化建议:
1. 非托管式事务(Unmanaged Transactions)需手动控制事务的提交(commit)和回滚(rollback),适合需要精细控制流程的场景。
示例代码:
const t = await sequelize.transaction();try { // 执行多个操作,传入事务对象 t const user = await User.create({ firstName: 'Bart', lastName: 'Simpson' }, { transaction: t }); await user.addSibling({ firstName: 'Lisa', lastName: 'Simpson' }, { transaction: t }); // 全部成功则提交 await t.commit();} catch (error) { // 任意失败则回滚 await t.rollback(); throw error; // 可选择抛出错误}特点:
通过回调函数自动处理提交/回滚,代码更简洁,推荐优先使用。
示例代码:
try { const result = await sequelize.transaction(async (t) => { const user = await User.create( { firstName: 'Abraham', lastName: 'Lincoln' }, { transaction: t } ); await user.setShooter( { firstName: 'John', lastName: 'Boothe' }, { transaction: t } ); return user; // 回调函数的返回值即为 result }); // 事务已自动提交} catch (error) { // 事务已自动回滚 throw error;}特点:
在订单支付成功后更新 Order 和 User 表时,托管事务可简化逻辑:
async function paidSuccess(outTradeNo, tradeNo, paidAt) { try { await sequelize.transaction(async (t) => { // 查询订单 const order = await Order.findOne({ where: { outTradeNo }, transaction: t }); // 避免重复处理 if (order.status > 0) return; // 更新订单状态 await order.update({ tradeNo, status: 1, paymentMethod: 0, paidAt }, { transaction: t }); // 更新用户会员信息 const user = await User.findByPk(order.userId, { transaction: t }); if (user.role === 0) user.role = 1; user.membershipExpiredAt = moment(user.membershipExpiredAt || new Date()) .add(order.membershipMonths, 'months') .toDate(); await user.save({ transaction: t }); }); } catch (error) { throw error; // 抛出错误供上层处理 }}关键点:
Sequelize 支持嵌套事务,但需注意默认行为(依赖 CLS 上下文):
sequelize.transaction((t1) => { return sequelize.transaction((t2) => { // 默认情况下,内部查询使用 t2 return Promise.all([ User.create({ name: 'Bob' }, { transaction: null }), // 显式禁用事务 User.create({ name: 'Mallory' }, { transaction: t1 }), // 指定外层事务 User.create({ name: 'John' }) // 默认使用 t2 ]); });});注意事项:
通过合理使用 Sequelize 的事务机制,可以高效保障 MySQL 操作的原子性和数据一致性。