IndexedDB是浏览器提供的本地非关系型数据库,支持大规模结构化数据存储,而事务处理是保障其数据操作原子性和一致性的关键机制。所有针对IndexedDB的数据读写操作都必须在事务上下文中执行,否则会直接抛出错误。

IndexedDB事务的基本概念
IndexedDB中的事务是一组不可分割的数据操作集合,事务内的所有操作要么全部成功执行,要么全部失败回滚,不会出现部分执行的情况。事务需要和指定的对象存储空间(Object Store)绑定,同时需要声明对应的操作模式。
事务的核心属性
- 作用范围:事务创建时需要指定关联的对象存储空间名称,事务只能操作这些指定空间内的数据。
- 操作模式:分为只读模式(readonly)和读写模式(readwrite),只读模式只能执行查询操作,读写模式可以执行增删改查操作。
- 事务状态:事务存在活跃、非活跃、提交中、回滚中、完成、错误等多个状态,状态变化决定了事务的生命周期。
创建IndexedDB事务的基本步骤
事务的创建需要先获取数据库实例,再通过数据库的transaction方法生成事务对象,最后通过事务对象获取对应的对象存储空间进行操作。
1. 打开数据库连接
首先需要通过indexedDB.open方法打开或创建数据库,拿到数据库实例后再创建事务:
// 打开名为testDB的数据库,版本号为1
const request = indexedDB.open('testDB', 1);
request.onupgradeneeded = function(event) {
const db = event.target.result;
// 创建名为user的对象存储空间,主键为id
if (!db.objectStoreNames.contains('user')) {
db.createObjectStore('user', { keyPath: 'id' });
}
};
request.onsuccess = function(event) {
const db = event.target.result;
console.log('数据库打开成功');
// 后续在此处创建事务
};
2. 创建事务并执行操作
通过数据库实例的transaction方法创建事务,第一个参数是关联的对象存储空间名称数组,第二个参数是事务模式:
request.onsuccess = function(event) {
const db = event.target.result;
// 创建读写模式的事务,关联user存储空间
const transaction = db.transaction(['user'], 'readwrite');
// 获取user对象存储空间
const store = transaction.objectStore('user');
// 添加数据
const addRequest = store.add({ id: 1, name: '张三', age: 20 });
addRequest.onsuccess = function() {
console.log('数据添加成功');
};
// 监听事务完成事件
transaction.oncomplete = function() {
console.log('事务执行完成');
};
// 监听事务错误事件
transaction.onerror = function(event) {
console.log('事务执行失败', event.target.error);
};
};
不同事务模式的使用场景
IndexedDB的事务模式决定了可以执行的操作类型,错误使用模式会导致操作失败:
| 事务模式 | 支持的操作 | 适用场景 |
|---|---|---|
| readonly | get、getAll、openCursor等查询操作 | 只需要读取数据,不需要修改数据的场景,比如页面加载时拉取本地缓存数据 |
| readwrite | add、put、delete等修改操作,以及所有查询操作 | 需要新增、修改或删除数据的场景,比如用户提交表单后更新本地存储 |
事务的错误处理与回滚机制
如果事务内的任意操作失败,整个事务会自动回滚,所有已经执行的操作都会失效,不会修改数据库中的数据。可以通过事务的onerror事件捕获错误,也可以在操作请求的onerror事件中处理具体错误:
const transaction = db.transaction(['user'], 'readwrite');
const store = transaction.objectStore('user');
// 尝试添加重复主键的数据,会触发错误
const addRequest = store.add({ id: 1, name: '李四', age: 22 });
addRequest.onerror = function(event) {
console.log('添加数据失败,主键已存在', event.target.error);
};
transaction.onerror = function() {
console.log('事务回滚,所有操作未生效');
};
事务使用的最佳实践
- 尽量缩小事务的作用范围,只关联需要操作的对象存储空间,减少锁的范围,提升并发性能。
- 优先使用只读模式执行查询操作,避免不必要的读写锁占用。
- 事务内的操作要尽可能简洁,不要在事务执行过程中穿插耗时的异步操作,否则可能导致事务超时。
- 始终监听事务的
oncomplete和onerror事件,确保知晓事务的最终执行结果。 - 不要在事务完成后再尝试操作事务关联的对象存储空间,此时事务已经结束,操作会无效。
需要注意的是,IndexedDB的事务是自动提交的,当所有操作完成且没有错误时,事务会自动提交,不需要手动调用提交方法。如果需要在事务中执行多个异步操作,要确保所有操作都在事务活跃状态下完成,否则事务会提前结束。