You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确处理expo-sqlite中transactionAsync的约束冲突错误?

Expo-SQLite transactionAsync 未抛出约束冲突错误导致事务挂起问题

我发现expo-sqlite的transactionAsync函数存在部分错误未被抛出的问题,尤其是UNIQUE约束和重复主键引发的错误。

当执行以下代码时,第一次插入操作正常完成,但第二次插入重复主键的executeSqlAsync会一直处于挂起状态:既不返回结果也不抛出错误,事务既不回滚也不提交,最终导致数据库被锁定。

export const insertMetricGroup = async (db: SQLite.SQLiteDatabase, name: string, description: string = '', grpOrder: number = 0) => {
    await db.transactionAsync(async (tx) => {
        const idFromIncrementTable = await getAndIncreaseIncrementAsync(tx, null, 'MetricGroupTable');
        console.log(idFromIncrementTable);
        // 第一次插入正常执行
        const res1 = await tx.executeSqlAsync(`INSERT INTO MetricGroupTable
        (id, name, description, grpOrder)
        VALUES (?, ?, ?, ?)`, [idFromIncrementTable, name, description, grpOrder]);
        console.log(res1);

        // 第二次插入因主键重复本应报错,但executeSqlAsync从未返回结果(res2不会被打印)
        const res2 = await tx.executeSqlAsync(`INSERT INTO MetricGroupTable
        (id, name, description, grpOrder)
        VALUES (?, ?, ?, ?)`, [idFromIncrementTable, name, description, grpOrder]);
        console.log(res2);
        await metricGroupReorder(tx);
      });
  };

getAndIncreaseIncrementAsync方法会从独立的增量表返回一个增量数值。

上述场景中,res2会一直处于待处理状态,无错误抛出也无结果返回,数据库表现为事务持续运行(被锁定),未执行回滚或提交。

在Android设备的Expo Go应用中重启后,任何插入表的尝试都会出现类似挂起情况(即使是无约束冲突的正常查询),进一步说明之前的事务无限期挂起。该问题会持续到删除数据库:

const db: SQLite.SQLiteDatabase = await SQLite.openDatabase('HabitsDb.db');
await db.closeAsync();
await db.deleteAsync();

我了解到transactionAsync曾返回结果错误或结果集,但大约一年前因相关问题进行了修改。JavaScript执行会在错误点完全停滞,这与我在其他项目中遇到的等待数据库锁释放的情况类似。

项目依赖版本如下:

"dependencies": {
  "@react-navigation/bottom-tabs": "^6.5.20",
  "@react-navigation/native": "^6.1.17",
  "@react-navigation/native-stack": "^6.9.26",
  "expo": "~50.0.17",
  "expo-status-bar": "~1.11.1",
  "react": "18.2.0",
  "react-native": "0.73.6",
  "react-native-popup-menu": "^0.16.1",
  "react-native-safe-area-context": "4.8.2",
  "react-native-screens": "~3.29.0",
  "expo-sqlite": "~13.4.0"
}

我尝试了以下低层级实现,仍出现相同结果,说明expo-sqlite库中修改返回类型的提交并非问题原因:

export const insertMetricGroupLowLevel = async(db: SQLite.SQLiteDatabase, name: string, description: string = '', grpOrder: number = 0) => {
// 校验数据
if (name.length < 1)
{
    throw('NAME TO SHORT');
}
await db.execAsync([{ sql: 'BEGIN;', args: [] }], false);
try
{
  const res1: any = await getAndIncreaseIncrementAsyncLowLevel(db, 'MetricGroupTable');
  await db.execAsync([{ sql: `INSERT INTO MetricGroupTable
  (id, name, description, grpOrder)
  VALUES (?, ?, ?, ?);`, args: [res1, name, description, grpOrder] }], false);
  await db.execAsync([{ sql: `INSERT INTO MetricGroupTable
  (id, name, description, grpOrder)
  VALUES (?, ?, ?, ?);`, args: [res1, name, description, grpOrder] }], false);
  await metricGroupReorderLowLevel(db);
  await db.execAsync([{ sql: 'END;', args: [] }], false);
  console.log('INSERTED METRIC GROUP')
}
catch(error)
{
  await db.execAsync([{ sql: 'ROLLBACK;', args: [] }], false);
  console.log('FAILED INSERT OF METRIC GROUP', error);
}

涉及的表结构:

CREATE TABLE IF NOT EXISTS MetricGroupTable (
      id INTEGER PRIMARY KEY,
      name TEXT NOT NULL,
      description TEXT NOT NULL,
      grpOrder INTEGER NOT NULL
    );

CREATE TABLE IF NOT EXISTS IncrementsTable (
      id INTEGER PRIMARY KEY,
      name TEXT NOT NULL,
      value INTEGER NOT NULL
    );

内容的提问来源于stack exchange,提问作者user22614887

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 19:42:06