Sqlite事务的读写操作

3

在Android中,是否可以使用事务来读取稍后将在同一事务中删除的值?

就像这样:

SQLiteDatabase db = helper.getReadableDatabase();
db.beginTransaction();

String whereClause = "COL1 = ? AND COL2 = ?";

// make a SELECT Query to read data 
Cursor c = db.rawQuery("SELECT * FROM Foo "+whereClause, whereValues);

...
// read Cursor data etc. 
...

db = helper.getWriteableDatabase();
// Delete data
db.delete("Foo", whereClause, whereValues);

db.setTransactionSuccessful();
db.endTransaction();

我想获取将要删除的数据,并通知UI组件该数据已被删除。

我使用多个线程,并希望确保在查询和删除之间没有其他表中的数据发生更改。

我可以在Java中使用synchronized等方式或锁定整个数据库(这只是作为最后一种选择),以实现此目的。

那么,对于这种情况,是否可以并且安全地使用事务?如果可以,如何在读取和写入事务中使用getWriteableDatabase()和getReadableDatabase()?

使用事务是否保证只删除先前查询过的数据?而不可能在SELECT查询和DELETE之间插入另一条数据记录(例如由其他线程插入)...我希望避免删除未被先前SELECT查询查询的数据记录,因为它是在SELECT查询之后和DELETE语句之前插入的。

示例:我有一个名为Foo的表:

 ID | COL1 | COL2
 ---|------|-----  
  1 |  1   |   1
 ---|------|-----
  2 |  0   |   1
 ---|------|-----
  3 |  1   |   1

假设我使用事务方式: 我将执行 SELECT * FROM Foo WHERE COL1 = 1 AND COL2 = 1。结果将返回 ID=1 和 ID=3 的行。
假设在执行 DELETE 语句之前,另一个线程插入了一行数据,使得表的内容变成下面这样:
 ID | COL1 | COL2
 ---|------|-----
  1 |  1   |   1
 ---|------|-----
  2 |  0   |   1
 ---|------|-----
  3 |  1   |   1
 ---|------|-----
  4 |  1   |   1

接下来将执行DELETE语句:DELETE FROM Foo WHERE COL1 = 1 AND COL2 = 1 所以ID为1、ID为3和ID为4的行将被删除。
但前面SELECT的结果是ID为1和ID为3的行。 所以我没有注意到失去了ID为4的行。
这正是我试图避免的。
我猜那将是使用锁的用例,但我不喜欢锁……
2个回答

4
尝试这个:

long id;
db = helper.getWriteableDatabase();

db.beginTransaction();
Cursor c = db.rawQuery("SELECT * FROM Foo " + whereClause, whereValues);
try {
    while (cursor.moveToNext()) {
        id = cursor.getLong(cursor.getColumnIndex("_id"));
        db.delete("Foo", "_id = " + id,null);
    }
    db.setTransactionSuccessful();

}catch {
            //Error in between database transaction 
}finally {
    db.endTransaction();

}

我不确定是否可以同时迭代游标并删除行。如果不行,您可以先将ID保存在数组中。

事务应该在 SELECT 之前开始。 - CL.
我已经尝试过并使用线程进行了测试。 SqliteOpenHelper使用单个连接到数据库文件。因此,多线程没有影响,因为所有SQL语句都是按顺序执行的。使用TRANSACTION确实会产生影响,即连接保留在TRANSACTION中,直到事务完成(提交或回滚)。所以你的答案是正确的。 - sockeqwe

3
您不能处理交易。如果您像这样使用它,它是完全无用的。只有在整个数据操作(在 try...catch 中)期间没有错误发生时,才应该提交(setTransactionSuccessful())。 还要注意,仅当进行多个数据操作时,事务才有用。SELECT 不会操作任何数据。所以,在所有这些离题之后,答案是……是的,您可以。

这只是一段代码片段,当然缺少异常处理...只是为了向您解释我想做什么。那么你是说使用事务可以保证只删除之前查询过的数据?而且不可能在SELECT查询和DELETE之间插入另一个数据记录(例如由另一个线程)...我希望避免删除未被先前SELECT查询查询的数据记录(因为它已在SELECT查询之后和DELETE语句之前插入)。 - sockeqwe
不。我的意思是你应该使用SELECT获取行ID,并使其唯一,然后删除该记录。毕竟,事务是为了在瞬间提交对表所做的所有更改或回滚所有更改而设计的。 - Phantômaxx
谢谢您的提示,但我已经明白了,那不是我想要问的。我已更新了我的问题并添加了一个示例。 - sockeqwe
你知道你只需要删除rowId = 1和rowId = 3的行。你可以在select返回的记录上循环,只删除那些记录。然后,你可以进行另一个select,只是为了计算是否还有剩余的记录(即第4条记录-因此,计数将> 0)。 - Phantômaxx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接