【问题标题】:Sqlite Transaction with read and write具有读写功能的 Sqlite 事务
【发布时间】:2016-06-09 16:25:49
【问题描述】:

是否可以在 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 的行,而没有注意到。

而这正是我想要避免的。

我猜这将是一个 LOCK 的用例,但我不喜欢 LOCKS ...

【问题讨论】:

    标签: android sqlite transactions


    【解决方案1】:

    试试这个:

    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之前开始。
    • 我已经尝试过了,并用线程对其进行了测试。 SqliteOpenHelper 使用单个连接到数据库文件。所以多线程没有影响,因为所有的sql语句都是按顺序执行的。使用 TRANSACTION 确实具有这样的效果,即在事务完成(提交或回滚)之前,为 TRANSACTION 保留连接。所以你的答案是正确的。
    【解决方案2】:

    不处理交易
    而且,如果你这样使用它,它完全没用
    只有在整个数据操作期间(在 try...catch 中)没有发生错误时,您才应该提交 (setTransactionSuccessful())。

    另请注意,事务仅在您进行多次数据操作时才有用。
    SELECT 不会操纵任何数据

    所以,在所有这些离题之后,答案是...... 是的,你可以

    【讨论】:

    • 这只是一个代码sn-p,当然缺少异常处理......这只是为了解释你我想要做什么。那么您是说使用事务可以保证删除之前查询过的那些数据吗?并且不可能在 SELECT 查询和 DELETE 之间插入另一个数据记录(例如由另一个线程)...我想避免删除上一个 SELECT 查询尚未查询的数据记录(因为它已在 SELECT 查询之后和 DELETE 语句之前插入)
    • 没有。我说您应该这样做,以便您使用 SELECT 获得 rowid 并且它是唯一的,然后删除该记录。毕竟,事务是为了提交您可以立即对表进行的所有更改或将它们全部回滚。
    • 感谢您的提示,但这对我来说已经很清楚了,而不是我想要的。我已经更新了我的问题并添加了一个示例
    • 你知道你只需要删除rowId = 1和rowId = 3的行。你可以对select返回的记录做一个循环,只删除那些记录。然后,您可能会再做一次选择,只是为了计算是否还有更多记录(即记录 4 - 因此,计数将 > 0)。
    猜你喜欢
    • 2020-07-20
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 2019-06-13
    • 2023-01-22
    • 1970-01-01
    相关资源
    最近更新 更多