【问题标题】:SQLiteDatabase nested transaction and workaroundSQLiteDatabase 嵌套事务和解决方法
【发布时间】:2014-09-29 18:15:49
【问题描述】:

已编辑:
是的,SQLite doesn't support 嵌套事务,但 docs 声明 SQLiteDatabase 确实如此。
情况
我有一个包含事务的方法,我需要从另一个事务中调用此方法。
此外 - 两个事务都在同一组记录上工作,但更新不同的列。
问题
看起来我的外部事务的结果被内部事务取消了,仍然被 setTransactionSuccessful() 标记为干净并由 endTransaction() 完成 - 我已经检查过了。
问题
- 知道为什么会发生这种情况吗?
- 有推荐的方法来进行此类交易吗?

【问题讨论】:

    标签: java android sqlite transactions


    【解决方案1】:

    “嵌套 Android 事务”使用 SQLites 嵌套事务/保存点支持。

    而是一个嵌套的 Android 事务抑制显示 SQLite 事务。嵌套事务不能自行回滚,因为它不存在于外部事务之外。这可以be seen heremTransactionStack == null 保护。

    实际上支持嵌套事务的唯一方法 - SQLite 支持,只是不支持 BEGIN/COMMIT - 是手动使用 SAVEPOINT/RELEASE 命令。当然,将代码设计为不依赖于此将消除所需的额外手动管理。

    (我可能会将所有事务工作从实际的单个操作中移出,将管理留给高级调用者;这对于 UoW 模式相当有效,但可能并不总是适用。)

    【讨论】:

      【解决方案2】:

      您可以使用 Android sqlite API 嵌套事务,使用 caveats

      事务可以嵌套。当外部事务结束时,该事务中完成的所有工作和所有嵌套事务都将被提交或回滚。如果任何事务在未标记为干净的情况下结束(通过调用 setTransactionSuccessful),则更改将回滚。否则他们将被提交。

      我见过的另一种通常用于 sqlite 的方法是传入一个布尔参数 isInTransaction,它告诉被调用的方法是应该自己处理事务,还是让调用者处理事务。

      【讨论】:

      • 谢谢。 Docs 允许嵌套事务,但看起来我的外部事务结果以某种方式被内部事务取消(每个事务处理相同的记录集,但更新不同的列)。 /顺便说一句,你知道这是怎么发生的吗??/现在我使用条件事务(类似于你的建议),所以我会坚持下去
      【解决方案3】:

      现在我最终得到了对称方法设计(类似于 laalto 建议),它提供了正确的工作。这是一种将嵌套事务显式“转换”为单个事务的解决方法。
      现在我可以单独调用它们或一个一个调用它们(没有我仍然不明白的副作用)。
      这里是:

          public void method1() {
          SQLiteDatabase db = dbhelper.getWritableDatabase();
      
          boolean doAsTransaction = !db.inTransaction();
      
          if (doAsTransaction)
              db.beginTransaction();
      
          try {
              // ...
              if (doAsTransaction)
                  db.setTransactionSuccessful();
      
          } catch (Exception e) {
              // ...
      
          } finally {
              // ...
              if (doAsTransaction)
                  db.endTransaction();
          }
      }
      
      
      public void method2() {
          SQLiteDatabase db = dbhelper.getWritableDatabase();
      
          boolean doAsTransaction = !db.inTransaction();
      
          if (doAsTransaction)
              db.beginTransaction();
      
          try {
              // ...
              method1();
              if (doAsTransaction)
                  db.setTransactionSuccessful();
      
          } catch (Exception e) {
              // ...
      
          } finally {
              // ...
              if (doAsTransaction)
                  db.endTransaction();
          }
      }
      

      【讨论】:

        【解决方案4】:

        您可以通过这种方式在execSQL() 中使用savepoints 的原始SQL 执行嵌套事务:

        db.execSql("SAVEPOINT test"); // declare savepoint
        // ... do some operations
        db.execSql(";ROLLBACK TO test"); // rollback
        db.execSql("RELEASE test"); // save changes
        

        ROLLBACK 前面的分号是必需的,因为没有它,Android 数据库框架将尝试调用endTransaction()。详见方法android.database.sqlite.SQLiteSession#executeSpecialandroid.database.sqlite.SQLiteSession#execute的代码。

        【讨论】:

        • DatabaseUtils 现在支持保存点回滚,但警告分号仍然需要在 API 级别 16-27 上正常工作。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-12
        • 1970-01-01
        • 2014-02-07
        • 1970-01-01
        相关资源
        最近更新 更多