Expo中React Native的SQLite事务真实性及提交、重试问题咨询
Expo React Native SQLite 事务详解
嘿,这个问题问到点子上了——很多人刚用expo-sqlite的时候都会疑惑事务的真实作用,我来给你拆解清楚:
1. RN中的SQLite事务是真实的SQLite事务,不是简单的连续调用
expo-sqlite底层直接对接了原生SQLite库,它提供的事务完全遵循SQLite的ACID特性:所有命令要么全部成功提交,要么全部失败回滚,绝对不是简单把几个SQL调用凑在一起执行。
举个直观的例子:
import * as SQLite from 'expo-sqlite'; const db = SQLite.openDatabase('myDB.db'); db.transaction(tx => { // 第一个操作:插入数据 tx.executeSql( 'INSERT INTO users (name) VALUES (?)', ['Alice'], () => console.log('插入成功') ); // 第二个操作:故意写错误SQL,触发失败 tx.executeSql( 'INSERT INTO non_existent_table (name) VALUES (?)', ['Bob'], () => console.log('这行不会执行'), (tx, error) => { console.log('事务失败:', error.message); return true; // 告知事务需要回滚 } ); }, (error) => { console.log('事务整体失败,已回滚'); }, () => { console.log('事务全部成功,已提交'); });
在这个例子里,第二个SQL执行失败后,第一个插入的Alice也会被回滚,数据库里不会留下这条数据——这就是真实事务的原子性体现。
2. 如何测试事务是否需要重试?
事务需要重试的场景大多是数据库锁冲突(比如多个事务同时修改同一张表)、SQLITE_BUSY或SQLITE_LOCKED错误。测试和实现重试可以这么做:
测试方法:
- 模拟并发冲突:写两个同时执行的事务,都修改同一条数据,其中一个会触发
SQLITE_BUSY,观察是否触发重试逻辑。 - 手动触发错误:在事务回调里故意抛出错误,或者执行会导致约束冲突的SQL(比如插入重复主键),看事务回滚后重试逻辑是否生效。
- 设置超时触发重试:通过
PRAGMA busy_timeout = 0;关闭默认超时,这样遇到锁会立刻返回错误,方便测试重试逻辑。
实现重试的代码示例:
const executeTransactionWithRetry = (db, transactionFn, retries = 3) => { return new Promise((resolve, reject) => { const attempt = (remainingRetries) => { db.transaction(tx => { transactionFn(tx); }, (error) => { // 检查是否是需要重试的错误码(SQLITE_BUSY=5, SQLITE_LOCKED=6) if ((error.code === 5 || error.code === 6) && remainingRetries > 0) { console.log(`事务失败,剩余重试次数:${remainingRetries - 1}`); setTimeout(() => attempt(remainingRetries - 1), 100); } else { reject(error); } }, () => { resolve(); }); }; attempt(retries); }); }; // 使用示例 executeTransactionWithRetry(db, (tx) => { tx.executeSql('UPDATE users SET score = score + 1 WHERE id = ?', [1]); }).then(() => { console.log('事务执行成功'); }).catch((error) => { console.log('重试多次后仍失败:', error); });
3. 不需要自行实现事务机制
expo-sqlite已经完整封装了SQLite的事务功能,你只需要按照官方文档的方式使用db.transaction()或db.readTransaction()(只读事务)即可。注意几个关键点:
- 事务内的所有SQL必须通过回调里的
tx对象执行,不能直接用db.executeSql(),否则不在事务上下文里。 - 如果需要回滚,在
executeSql的错误回调里返回true即可。
内容的提问来源于stack exchange,提问作者Michel
相关产品推荐
相关产品推荐

