房间迁移 - 在迁移代码中使用 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 查询结果为:-
你的代码可以是:-
// 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;
这是我的迁移更改:
基本上重命名然后更新一些值 基于行 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 查询结果为:-
你的代码可以是:-
// 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;