事务
RxDB 提供了事务机制来保证多个数据库操作的原子性。事务系统由适配器实现,核心通过事件总线协调事务期间的事件派发。
事务概述
事务确保一组数据库操作要么全部成功(提交),要么全部撤销(回滚)。RxDB 的事务机制基于两个层面:
- 数据库层:适配器(如 SQLite)使用底层数据库的
BEGIN / COMMIT / ROLLBACK - 事件层: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 适配器的事务流程:
- 获取数据库连接并加锁(
#transaction_lock = true) - 派发
TRANSACTION_BEGIN事件 - 执行
BEGIN; PRAGMA defer_foreign_keys = ON; - 执行用户传入的事务函数
- 成功则
COMMIT并派发TRANSACTION_COMMIT - 失败则
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 状态不会受到影响(缓冲事件被丢弃)