跳到主要内容

事务

RxDB 提供了事务机制来保证多个数据库操作的原子性。事务系统由适配器实现,核心通过事件总线协调事务期间的事件派发。

事务概述

事务确保一组数据库操作要么全部成功(提交),要么全部撤销(回滚)。RxDB 的事务机制基于两个层面:

  1. 数据库层:适配器(如 SQLite)使用底层数据库的 BEGIN / COMMIT / ROLLBACK
  2. 事件层:RxDB 核心在事务期间缓冲所有实体变更事件,事务成功后才统一派发

事件缓冲机制

当事务进行中时,RxDB 会暂停派发所有非事务事件。这避免了 UI 层接收到中间状态的数据变更通知:

TRANSACTION_BEGIN

├── create(entityA) → 事件缓冲,不派发
├── update(entityB) → 事件缓冲,不派发
├── remove(entityC) → 事件缓冲,不派发

├── TRANSACTION_COMMIT → 统一派发所有缓冲事件
│ OR
└── TRANSACTION_ROLLBACK → 丢弃所有缓冲事件

内部实现

// RxDB 核心事件派发逻辑
dispatchEvent(event: RxDBEvent): void {
// 在事务期间缓冲非事务事件
if (this.#transaction_event_pending && !isTransactionEvent(event)) {
this.#need_dispatch_events.push(event);
} else {
// 立即派发
this.#listener(event.type).forEach(listener => listener.call(this, event));
}
}
  • TRANSACTION_BEGIN — 设置 #transaction_event_pending = true,后续实体事件进入缓冲队列
  • TRANSACTION_COMMIT — 依次派发缓冲队列中的所有事件,然后清空队列
  • TRANSACTION_ROLLBACK — 直接丢弃缓冲队列,不派发任何事件

适配器中的事务

不同适配器以不同方式实现事务。以 SQLite 适配器为例:

// SQLite 适配器的 transaction 方法
const result = await adapter.transaction(async client => {
// 这里的所有操作在一个数据库事务中执行
await client.execute('INSERT INTO ...');
await client.execute('UPDATE ...');
// 如果抛出异常,自动回滚
return result;
});

SQLite 适配器的事务流程:

  1. 获取数据库连接并加锁(#transaction_lock = true
  2. 派发 TRANSACTION_BEGIN 事件
  3. 执行 BEGIN; PRAGMA defer_foreign_keys = ON;
  4. 执行用户传入的事务函数
  5. 成功则 COMMIT 并派发 TRANSACTION_COMMIT
  6. 失败则 ROLLBACK 并派发 TRANSACTION_ROLLBACK,抛出错误

事务日志

SQLite 适配器支持事务日志(transactionLog 参数),会将成功的事务操作记录到 RxDBChange 表中,用于分支切换和版本管理等场景。

事件监听

可以监听事务事件来执行自定义逻辑:

import { TRANSACTION_BEGIN, TRANSACTION_COMMIT, TRANSACTION_ROLLBACK } from '@aiao/rxdb';

// 监听事务提交
rxdb.addEventListener(TRANSACTION_COMMIT, () => {
console.log('事务已提交,数据已持久化');
});

// 监听事务回滚
rxdb.addEventListener(TRANSACTION_ROLLBACK, () => {
console.log('事务已回滚,数据变更已撤销');
});
最佳实践
  • 将多个相关的数据变更放在同一个事务中,确保数据一致性
  • 事务期间 UI 不会收到中间状态的通知,避免闪烁
  • 事务回滚后,UI 状态不会受到影响(缓冲事件被丢弃)