重新打开ROOM数据库。

8

我将尝试关闭并重新打开Room数据库。(目的是备份SQLite文件)

这是我如何关闭它:

public static void destroyInstance() {
    if (INSTANCE != null && INSTANCE.isOpen()) {
        INSTANCE.close();
    }
    INSTANCE = null;
}

INSTANCE是一个RoomDatabase对象。

为了重新打开,我通过调用以下代码来初始化INSTANCE对象:

Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, C.ROOM_DB_NAME)

当我切换到另一个活动后,在logcat中看到以下错误:E/ROOM: Invalidation tracker is initialized twice

SELECT查询正常工作,但插入失败并显示以下错误:

E/SQLiteLog: (1) no such table: room_table_modification_log

E/ROOM: Cannot run invalidation tracker. Is the db closed?
java.lang.IllegalStateException: The database '/data/user/0/ro.example.example/databases/mi_room.db' is not open.

尽管INSTANCE.isOpen()返回true

Room版本:1.1.1

有人知道这个“room_table_modification_log”表是干什么的吗?


你解决了这个问题吗?我在 Room 2.2.5 中仍然遇到这个错误。 - MickeyR
没有解决方案。我只是不再关闭数据库了。如果你想将数据库文件复制到另一个位置,你可以查看下面的答案(schv09的答案)。 - Alex Busuioc
@MickeyR,你有解决这个问题的方法吗?我也遇到了这个错误。 - Muazzam A.
有什么解决办法吗,@AlexBusuioc?我在2.3.0版本中遇到了这个错误。 - Muazzam A.
1
没有重新打开以前关闭的房间数据库的解决方案。我只是不再关闭DB了。可能有帮助的是检查@schv09的答案或这个问题:https://dev59.com/xlUL5IYBdhLWcg3wB0BJ - Alex Busuioc
@MuazzamA。这对我来说是解决方案:https://stackoverflow.com/questions/63957123/room-db-file-import-error-no-such-table-room-table-modification-log/64054341#64054341 - MickeyR
3个回答

4

针对未来的读者:你不需要关闭数据库即可将文件复制到另一个位置(创建备份)。

Android实现SQLite数据库通常处于WAL(预写日志)模式。这在后台使用3个文件:第一个文件名是你的数据库名称(即my_db),第二个带有该名称和“-shm”后缀(my_db-shm),第三个带有“-wal”后缀(my_db-wal)。-wal文件将保存更改内容。

如果您想要使用默认路径为您的数据库(my_db文件)进行备份,您需要确保其与最新交易同步。通过在数据库上运行检查点可以实现此目的。之后,您可以将此文件复制到手机上所需的位置,然后继续使用您的数据库而无需出现问题。在这个问题的被接受的答案中很好地解释了这一点:

但如果将所有内容移动到原始数据库文件中是您想要做的事情,那么您就不必关闭数据库。

您可以使用wal_checkpoint特权强制执行检查点。针对数据库查询以下语句。由于Room不支持pragma(它将触发UNKNOWN query type error),因此我们在此使用原始查询。

将此查询放在您的DAO中:

@RawQuery
int checkpoint(SupportSQLiteQuery supportSQLiteQuery);

并且,当你调用checkpoint方法时,请使用查询语句:
myDAO.checkpoint(new SimpleSQLiteQuery("pragma wal_checkpoint(full)"));

很棒的答案!但是,如果我想在检查点之后清除当前数据库并将数据库基本文件复制到存储中,会发生什么?我使用rxjava - 是否没有保证,某些线程不应该在“检查点”和“复制DB”函数之间写入一些内容到数据库中,结果,我不确定我是否清理了未移动到存储的数据。你怎么看?谢谢! - dr_begemot
更新:我觉得我应该在beginTransaction()和endTransaction()块中使用这些操作,而不是使用DAO,使用原始的“query”(因为DAO将所有内容都包装在begin/endTransaction()中 - 而在这种情况下并非如此),你认为呢,我的想法正确吗? - dr_begemot
对不起,dr_begemot,我无法在这里帮助你,因为我不使用rxjava。希望你很快找到解决方案! - schv09
1
@schv09 我知道这是一篇旧帖子,但是否有一种方法可以重置数据库实例而不必关闭应用程序并重新打开它? 当我还原数据库时,所有的流程仍在使用旧实例的数据库,我必须关闭打开的应用,并将其从最近使用中删除,然后再次打开它以获取已还原的数据。 - Martin
@马丁,你找到解决办法了吗? - Captain Jacky
@Martin,你解决这个问题了吗? - undefined

0
经过许多天的努力,我终于找到了解决方法,您需要在 onOpen 数据库回调函数中创建表格 room_table_modification_log
示例如下:
private fun buildDatabase(context: Context): MainDatabase {
            return Room.databaseBuilder(
                context.applicationContext,
                MainDatabase::class.java,
                databaseName
            ).addMigrations(MIGRATION_2_3)
                .addCallback(getCallback())
                .build()
        }

并且像这样实现getCallback()函数:

fun getCallback(): Callback {
    return object : Callback() {
        override fun onOpen(db: SupportSQLiteDatabase) {
            super.onOpen(db)
                db.execSQL("CREATE TEMP TABLE room_table_modification_log(table_id INTEGER PRIMARY KEY, invalidated INTEGER NOT NULL DEFAULT 0)")          
            }
        }
}

完成上述步骤后,您将无法再遇到错误room_table_modification_log


0

将您的 Room 版本降级至 1.1.1-rc1,问题就会消失。请注意更新,因为这是 1.1.1 中的一个错误。


刚刚测试了一下,不行,这并没有解决问题。行为完全相同。 - Alex Busuioc
我记得我也遇到了同样的问题。这篇帖子解决了我的问题,也帮助了其他人 https://dev59.com/QlUL5IYBdhLWcg3wbni4 - Zun
确实,这两个问题似乎是相关的。它们出现的上下文是不同的(在我的情况下是关闭-重新打开数据库;在另一个帖子中是数据库迁移)。不幸的是,我的问题在所有 Room 版本上都会出现。 - Alex Busuioc

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