房间迁移 - 在迁移代码中使用 Cursor 是否安全(获取行 ID)?

Room migration - Is it safe to use Cursor in migration code (to get row ID)?

这是我的迁移更改:

基本上重命名然后更新一些值 基于行 id 其中 isNext = 1.


我想我需要使用 Cursor,如 所示。所以我的自动迁移代码看起来像这样:

// For Auto-Migration
@RenameColumn(tableName = "Notifications", fromColumnName = "isNext", toColumnName = "wasShown")
static class MyAutoMigration implements AutoMigrationSpec {
    @Override
    public void onPostMigrate(@NonNull SupportSQLiteDatabase database) {
        // Invoked once auto migration is done
        Cursor cursor = database.query("SELECT * from Notifications WHERE wasShown = 1");
        String row_id = String.valueOf(cursor.getPosition());
        database.execSQL("UPDATE Notifications SET wasShown = 1 WHERE id < ?", new String[]{row_id});
        database.execSQL("UPDATE Notifications SET wasShown = 0 WHERE id = ?", new String[]{row_id});
        cursor.close();
    }
}

在迁移代码中使用这样的逻辑是否存在某种“风险”?
例如,因为它可能会抛出异常,或者发生其他会使应用程序崩溃的事情。

我以前从未通过老式游标直接 SQL 操作过,所以我觉得这种逻辑可能很脆弱。

应该没有问题。但是,没有必要因为 UPDATE 可以在不需要 Cursor 的情况下完成,因为 UPDATE 可以确定值。

例如SQL 可能是:-

WITH isNext(id) AS (SELECT id FROM Notifications WHERE wasShown = 1 LIMIT 1)
UPDATE Notifications SET wasShown = CASE WHEN id < (SELECT id FROM isNext) THEN 1 ELSE 0 END;
  • isNext 是一个 CTE (Common Table Expression),就像一个临时的 table,名字可以随便起,isNext 是因为它是历史的列的名称。
  • 请注意,上面的内容即使超过两行,也是一个查询。

下面演示上面的内容:-

DROP TABLE IF EXISTS Notifications;
CREATE TABLE IF NOT EXISTS Notifications (id INTEGER PRIMARY KEY, wasShown INTEGER);
INSERT INTO Notifications (wasShown) VALUES (0),(0),(0),(0),(1),(0),(0),(0),(0),(0);
/* Show the pre-update data */
SELECT * FROM Notifications;

/* Data prepared so the actual UPDATE */
WITH isNext(id) AS (SELECT id FROM Notifications WHERE wasShown = 1 LIMIT 1)
UPDATE Notifications SET wasShown = CASE WHEN id < (SELECT id FROM isNext) THEN 1 ELSE 0 END;

/* Show the post-update data */
SELECT * FROM Notifications;
/* cleanup demo */
DROP TABLE IF EXISTS Notifications;

pre-update 查询结果为:-

post-date 查询结果为:-

Here's an SQLFiddle Demo

你的代码可以是:-

// For Auto-Migration
@RenameColumn(tableName = "Notifications", fromColumnName = "isNext", toColumnName = "wasShown")
static class MyAutoMigration implements AutoMigrationSpec {
    @Override
    public void onPostMigrate(@NonNull SupportSQLiteDatabase database) {
        // Invoked once auto migration is done
        database.execSQL("WITH isNext(id) AS (SELECT id FROM Notifications WHERE wasShown = 1 LIMIT 1) UPDATE Notifications SET wasShown = CASE WHEN id < (SELECT id FROM isNext) THEN 1 ELSE 0 END;");

    }
}

我不得不调整 MikeT 的出色答案以在我的迁移中再容纳一个 use-case:
当旧 table 中没有 isNext=1 的值时,新 table 中的所有值都必须设置为 wasShown=1.

经过反复试验,我使用以下条件逻辑来涵盖我的所有案例:

WITH isNext(id) AS (SELECT id FROM Notifications WHERE wasShown = 1 LIMIT 1)
UPDATE Notifications SET wasShown = 
CASE WHEN EXISTS(SELECT id FROM Notifications WHERE wasShown = 1 LIMIT 1)
THEN CASE WHEN id < (SELECT id FROM isNext) THEN 1 ELSE 0 END
ELSE 1 END;