这个Database Transaction功能多多,你用过吗?

Vona 是一款直观、优雅、强大的 Node.js Web 框架,用于快速开发任何规模的企业级应用。首创 DTO 动态推断与生成能力,从而显著提升开发效率和开发体验。Vona ORM 对数据库事务提供了完整的支持,提供了直观、优雅、强大的特性:

  1. 使用装饰器启用事务
  2. 事务传播机制
  3. 事务补偿机制
  4. 确保数据库与缓存数据一致性

使用装饰器启用事务

typescript 复制代码
import { Database } from 'vona-module-a-orm';

class ServicePost {
  @Database.transaction()
  async transaction() {
    // insert
    const post = await this.scope.model.post.insert({
      title: 'Post001',
    });
    // update
    await this.scope.model.post.update({
      id: post.id,
      title: 'Post001-Update',
    });
  }
}  

手工启用事务

1. 使用当前数据源

typescript 复制代码
class ServicePost {
  async transactionManually() {
    const db = this.bean.database.current;
    await db.transaction.begin(async () => {
      await this.scope.model.post.update({ id: 1, title: 'Post001_Update' });
    });
  }
}

2. 使用指定数据源

typescript 复制代码
class ServicePost {
  async transactionManually() {
    const db = this.bean.database.getDb({ clientName: 'default' });
    await db.transaction.begin(async () => {
      const modelPost = this.scope.model.post.newInstance(db);
      await modelPost.update({ id: 1, title: 'Post001_Update' });
    });
  }
}

事务参数

diff 复制代码
class ServicePost {
  @Database.transaction({
+   isolationLevel: 'READ_COMMITTED',
+   propagation: 'REQUIRED'
  })
  async transaction() {
    ...
  }
}  
diff 复制代码
class ServicePost {
  async transactionManually() {
    const db = this.bean.database.getDb({ clientName: 'default' });
    await db.transaction.begin(
      async () => {
        ...
      },
      {
+       isolationLevel: 'READ_COMMITTED',
+       propagation: 'REQUIRED',
      }
    );
  }
}  

事务参数:isolationLevel

名称 说明
DEFAULT 数据库相关的缺省isolationLevel
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE
SNAPSHOT

事务参数:propagation

Vona ORM 支持数据库事务传播机制

名称 说明
REQUIRED 默认的事务传播级别。如果当前存在事务, 则加入该事务。如果当前没有事务, 则创建一个新的事务
SUPPORTS 如果当前存在事务,则加入该事务. 如果当前没有事务, 则以非事务的方式继续运行
MANDATORY 强制性。如果当前存在事务, 则加入该事务。如果当前没有事务,则抛出异常
REQUIRES_NEW 创建一个新的事务。如果当前存在事务, 则把当前事务挂起。也就是说不管外部方法是否开启事务,总是开启新的事务, 且开启的事务相互独立, 互不干扰
NOT_SUPPORTED 以非事务方式运行。如果当前存在事务,则把当前事务挂起(不用)
NEVER 以非事务方式运行。如果当前存在事务,则抛出异常

事务补偿机制

当事务成功或者失败时执行一些逻辑

1. 成功补偿

typescript 复制代码
this.bean.database.current.commit(async () => {
  // do something when success
});

2. 失败补偿

typescript 复制代码
this.bean.database.current.compensate(async () => {
  // do something when failed
});

事务与Cache数据一致性

许多框架使用最简短的用例来证明是否高性能,而忽略了业务复杂性带来的性能挑战。随着业务的增长和变更,项目性能就会断崖式下降,各种优化补救方案让项目代码繁杂冗长。而 Vona 正视大型业务的复杂性,从框架核心引入缓存策略,并实现了二级缓存Query缓存Entity缓存等机制,轻松应对大型业务系统的开发,可以始终保持代码的优雅和直观

Vona 系统对数据库事务与缓存进行了适配,当数据库事务失败时会自动执行缓存的补偿操作,从而让数据库数据与缓存数据始终保持一致

针对这个场景,Vona 提供了内置的解决方案

1. 使用当前数据源

typescript 复制代码
class ServicePost {
  @Database.transaction()
  async transaction() {
    // insert
    const post = await this.scope.model.post.insert({
      title: 'Post001',
    });
    // cache
    await this.scope.cacheRedis.post.set(post, post.id);
  }
}  
  • 当新建数据后,将数据放入 redis 缓存中。如果这个事务出现异常,就会进行数据回滚,同时缓存数据也会回滚,从而让数据库数据与缓存数据保持一致

2. 使用指定数据源

typescript 复制代码
class ServicePost {
  async transactionManually() {
    const db = this.bean.database.getDb({ clientName: 'default' });
    await db.transaction.begin(async () => {
      const modelPost = this.scope.model.post.newInstance(db);
      const post = await modelPost.insert({ title: 'Post001' });
      await this.scope.cacheRedis.post.set(post, post.id, { db });
    });
  }
}  
  • 如果对指定的数据库进行操作,那么就需要将数据库对象db传入缓存,从而让缓存针对数据库对象db执行相应的补偿操作。当数据库事务回滚时,让数据库数据与缓存数据保持一致
相关推荐
Darling噜啦啦4 小时前
NestJS 全栈 CRUD 实战:从 Module 拆分到 RESTful 七大装饰器,彻底搞懂后端接口工程
nestjs
嘟嘟07177 小时前
从浏览器到 hello world:Docker + nginx 反向代理 + Node 最小服务器一次讲清
docker·容器·node.js
不好听6137 小时前
TypeScript 面试必会:工具类型全家桶,从 keyof 到手写 Partial
面试·typescript
FungLeo7 小时前
Node 后端实战 · 老系统数据迁移怎么不出乱子?V1→V2 重构实战与 3 个生产坑
node.js·serverless·数据库迁移·d1 数据库
嘟嘟07177 小时前
从入口到 CRUD:用 NestJS 的模块化与装饰器思想读懂一个后端应用
后端·typescript·nestjs
嘟嘟07177 小时前
TypeScript 工具类型通关:Pick、Omit、Partial、Record 区别与 Omit 等价实现一次讲清
设计模式·面试·typescript
今日无bug7 小时前
Tool Use 工具调用 —— 让LLM缸中大脑突破物理限制的3个阶段
node.js·llm·agent
想要成为糕糕手7 小时前
🚀 NestJS 完全入门指南: Module/Controller/Service讲解 + 实战 CRUD
后端·nestjs
何时梦醒7 小时前
TypeScript 工具类型一篇讲透:Pick、Omit、Partial、Exclude、Record、ReturnType、keyof
前端·面试·typescript
小林ixn7 小时前
TS 工具类型实战:Pick/Omit/Partial/Record 一次讲透,别再 Omit 反了
typescript·代码规范