首页 > 解决方案 > SQLite - 同一事务中的多个调用

问题描述

感谢您的关注 - 我正在使用 Typescript 编写前端 SPA。我正在使用电容器JS 来实现跨平台兼容性,并且我正在使用@capacitor-community/sqlite 插件。我是 SQLite 的新手,但不是一般的数据库。

通过在 SQLite 上使用特定的 API,这可能会变得复杂。
API 在这里:https ://github.com/capacitor-community/sqlite#supported-methods

无论如何,API 不符合我的期望。

我想要一种能够启动事务、进行多次调用并最终提交事务(或在错误处理程序中回滚)的模式。如果我可以在同一个 txn 内调用读取,或者至少能够在写入 txn 打开时在 txn外部进行读取,那就太好了。

看起来这个 (capacitor-community/sqlite) 包装器默认情况下在我所做的每个调用周围添加一个事务,除非我用 bool 参数覆盖。如果我覆盖,我负责将事务代码包含在 sql 语句块中。

我的第一个天真的方法是发出一个将启动事务的语句,然后它会进行额外的 api 调用,这些调用应该是 txn 的一部分,最后运行一个 sql 语句来commit处理 txn。
例如

async Transaction(actions: () => Promise<void>): Promise<void> {
    await SqliteService.Instance.db.execute('BEGIN TRANSACTION;', 
    try {
      await actions();
      await SqliteService.Instance.db.execute('COMMIT TRANSACTION;', false); //useTransaction = false
    } catch (e) {
      await SqliteService.Instance.db.execute('ROLLBACK TRANSACTION;', false); //useTransaction = false
      throw e;
    }

这根本不起作用。看起来像这样的状态并没有在通话之间保持。我认为有些事情(比如打开光标)会导致会话保持打开状态,但通常会话只会随着我对 API 的每次调用而打开和关闭。
我没有尝试过使用游标来保持交易开放,这似乎是一种反模式

当我仔细考虑时,似乎我可能必须在事务中发送一大块 SQL 才能让 SQLite 做我想做的事。

所以我正在考虑将我的 documentStore api 重写为,而不是直接调用 SQLite 来运行语句,而是当我想在单个事务中执行多项操作时,提供一个 sqlBuilder 类型的类。然后我可以将多个语句粘合在一起。

...

但是我喜欢当前的界面,并且重写我的 documentStore 上的“写入”方法以拥有一些 sqlBuilder 似乎是朝着不同方向迈出的一大步。例如

async Transaction(sqlBuilder: (builder: WriteSqlBuilder<T>) => {statement: string, values: unknown[]} []) {
...

我想知道 - 人们通常如何在单个事务中在 SQLite 中执行多个操作(查询、写入)?

谢谢!

标签: typescriptsqlitecapacitor

解决方案


因此,这最终成为我使用的包装器中的一些特殊行为capacitor-community/sqlite

当调用 eg 时,executeSet({sql, params}[], transaction)您可以传递一个布尔值来使用或不使用事务。这是一个帮手。此transaction值默认为 true。当它设置为 true 时,包装库会自动为您添加BEGIN TRANSACTIONCOMMIT TRANSACTION(并且ROLLBACK TRANSACTION在出现错误的情况下)围绕您的 sql 项,所有这些都在单个调用中。

如果你显式设置transaction为 false,它将像普通 SQLite 一样,为调用启动一个隐式事务,当调用完成时,这是最后一个 SQL 语句,隐式事务被隐式提交。

让我感到困惑的是我正在 Web 浏览器中进行开发工作(对于这个跨平台应用程序),所以我正在使用jeep-sqlite. jeep-sqlite使用内存中的 sqlite db,并有一种saveToStore(dbName)方法将 SQLite 字节刷新到 IndexedDb 条目中。

我认为正在发生的事情是我在交易中间冲洗。刷新后,不再有活动事务。


最后,我使用async-lock了对 SQLite db 包装器的访问权限,并拥有自己async Transaction(actions: () => Promise<void>)处理 BEGIN/COMMIT/ROLLBACK 本身的方法,以及一些通用的 DocumentStore 方法,这些方法进行隐式调用,然后包含在事务中。


推荐阅读