uniapper
uniapper
  • 发布:2019-05-29 18:37
  • 更新:2019-12-31 11:08
  • 阅读:4303

【sqlite事务】sqlie所有操作都是异步的(包括事务),如何确定commit/rollback的时机?

分类:5+ SDK

例如:
分别向3个表各插入1条数据。
plus.sqlite.transaction 是异步的。
plus.sqlite.execSql 是异步的。

类似下面的逻辑肯定是不行的。那么,这个”事务“该如何处理?或者有详细的例子参考吗?

plus.sqlite.transaction({  
  name: dbname,  
  operation: 'begin',  
  success: function () {},  
  fail: function (err){}  
})  
try{  
  plus.sqlite.executeSql(dbname, sql_table1)  
  plus.sqlite.executeSql(dbname, sql_table2)  
  plus.sqlite.executeSql(dbname, sql_table3)  
  plus.sqlite.transaction({  
    name: dbname,  
    operation: 'commit',  
    success: function () {},  
    fail: function (err){}  
  })  
}catch(err) {  
  plus.sqlite.transaction({  
    name: dbname,  
    operation: 'rollback',  
    success: function () {},  
    fail: function (err){}  
  })  
}
2019-05-29 18:37 负责人:无 分享
已邀请:
uniapper

uniapper (作者) - abc

顶一下

uniapper

uniapper (作者) - abc

谁有实例代码?

DCloud_App_Array

DCloud_App_Array

所有方法都有成功回调,触发成功回调就说明异步操作完成。

uniapper

uniapper (作者) - abc

@DCloud_App_Array

这个“成功回调”我理解。
问题是,begin/ CUD sql / commit / rollback 的执行顺序。

就像上面的代码,begin/ CUD sql / commit / rollback都是异步执行。
begin和自己的CUD操作先后顺序还好确定。可以在begin的success回调中执行CUD的多条sql。
但是,

  1. 如何保证commit 在CUD的sql全部完成后才执行?
  2. 在CUD sql执行的过程中如何保证任何一条出错后立即rollback,且是立即终止其他未完成的CUD的sql之后执行rollback?
    (若在每条executeSql()的fail回调中调用rollback,可能会导致rollback后其他仍未执行完成的executeSql() 在没有触发自己的fail回调时,成功完成对数据库的修改)
uniapper

uniapper (作者) - abc

求解答

uniapper

uniapper (作者) - abc

摸索出来怎么执行事务了。但是却不敢用事务了!
以下是自己通过多次实验对执行原理的猜测:

plus.sqlite sdk的api都是“异步执行”但也是“同步执行”。
异步执行:
相对于同逻辑模块的非plus.sqlite代码,所有的plus.sqlite代码是异步的。类似于,放到一个异步队列中。
同步执行:
相同逻辑模块中的所有plus.sqlite代码,又是按照其在代码中出现的位置,严格按照先后顺序“同步执行”的。

其实,这些原理官方应该在文档中有个明确的说明。文档差没关系,但是这些关键的原理不能靠开发者去猜测吧。

通过封装成Promise也解决了,多条CUD sql语句执行过程中某一条或几条失败,也可以触发真正意义上的rollback了。

但是!!!
事务执行完毕后,会被锁库(database is locked).这个库就废了。
无论是通过sdk的原生写法还是自己封装的Promise,都会锁库。

而,plus.sqlite.transaction 中的operation中开始事务只能用commit,无法执行 commit immediate等因而无法尝试其他的sqlite锁形式...

uniapper

uniapper (作者) - abc

 但是!!!  
事务执行完毕后,会被锁库(database is locked).这个库就废了。  
无论是通过sdk的原生写法还是自己封装的Promise,都会锁库。

这个“锁库”的原因,找到了:

在执行CUD sql的过程中发生了异常,没有走到commit/rollback
并且,在CUD的异常处理函数中关闭了数据库连接。

即: 事务没有commit/rollback的情况下关闭了连接,导致sqlite的锁没有释放!

话说, @DCloud_App_Array plus.sqlite.closeDatabase中加上end transatcion的相关操作有没有可行性?

2***@qq.com

2***@qq.com

哥们能给个例子吗?

  • uniapper (作者)

    就按上面写的, 只开启事务,然后执行插入等sql,之后立即关闭(closeDatabase).既不commit也不rollback。在执行完第一次后,之后就会提示 database is locked。

    2019-06-09 10:39

  • 2***@qq.com

    回复 uniapper: 你的异常 没有进 catch??? 那里不是rollback了吗

    2019-06-09 21:13

  • uniapper (作者)

    回复 2***@qq.com: 我的问题中只是当时问问题时随便写的(而且executeSql的传参方式也写错了)。当时实际写逻辑时,因为涉及的逻辑代码较多导致出错没有捕捉对异常。你复现“锁库”可以尝试不写 commit/rollback直接closeDatabase试一下。

    2019-06-10 10:55

  • 2***@qq.com

    回复 uniapper: 我现在是 打开成功,开始事务成功,执行SQL成功,提交事务成功,关闭数据库成功, 再次执行SQL的时候 打开数据库 就锁库了 ,都不知道为什么了

    2019-06-11 00:31

  • uniapper (作者)

    回复 2***@qq.com: 你可以新开一贴,把你的测试代码贴出来然后@官方看看是啥情况

    2019-06-11 09:27

  • 2***@qq.com

    回复 uniapper: 这个问题已经解决了是 事务并发的问题,只要保证同时别有两个事务同时存在就行了。但是 锁库有什么办法解锁你知道吗?除了重启程序的话

    2019-06-14 02:56

  • uniapper (作者)

    回复 2***@qq.com: 是的,并发操作容易出问题,这是sqlite的特性决定的。我暂时没再敢用事务。当时发现自己这边是因为没有commit/rollback导致的锁库后,简单写了个插入数据的事务测试执行了多次(单webview内)并未复现“锁库”。 建议你的情况,单独开帖问官方。

    2019-06-14 08:43

  • 2***@qq.com

    回复 uniapper: 你是直接 读写数据库,不用事务么?那样不会锁库是吗

    2019-06-18 01:25

  • uniapper (作者)

    回复 2***@qq.com: 现在没用事务,还没遇到过锁库的情况。

    2019-06-18 08:24

n***@qq.com

n***@qq.com

哥们能给个例子吗?

  • uniapper (作者)

    就按上面写的, 只开启事务,然后执行插入等sql,之后立即关闭(closeDatabase).既不commit也不rollback。在执行完第一次后,之后就会提示 database is locked。

    2019-06-09 10:39

8***@qq.com

8***@qq.com - 初来咋到

我觉得可以使用promise或async await做这块,通过传入一个存有sql的数组,循环执行,html5+这边的success和fail执行resolve和reject,在promise的catch区间执行回滚,在最后的一条sql执行完后,进行commit,另外我想请教下,您们是怎么获取最后执行的sql的id的,我现在想到的是使用事务的情况,想请教下楼主怎么解决并发和获取最后执行的sql的id(程序可能会进行数据下拉、上传的操作、人为操作,这些会导致并发的问题)

  • 8***@qq.com

    使用promise.all

    2019-06-19 11:20

  • uniapper (作者)

    回复 8***@qq.com:

    参考以下MDN上的Promise.all 的文档

    Promise.all


    > 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)

    2019-06-19 14:43

  • 8***@qq.com

    回复 uniapper: 嗯嗯,我刚刚尝试了异步执行多条事务,发现这几条执行后获取到的都是最后一个id,而不是当前事务中获取的id,唉,executeSql成功没有参数返回真伤啊。。

    2019-06-19 15:43

w***@163.com

w***@163.com - 90后程序员

看了一下官方的关于sqlite数据库操作的例子,发现没有关于数据事务操作的用法,估计这个事务操作还是存在很多问题的,我还是先不用了吧

该问题目前已经被锁定, 无法添加新回复