在房间数据库中调用“插入”不会完成事务

ber*_*o.g 4 android kotlin android-room

@insert在 Room 数据库中执行简单操作时遇到问题。这些是我的课程:

模型类

@Entity(tableName = "my_model")
data class MyModel(
    @PrimaryKey @ColumnInfo(name = "id_model") var uid: Int,
    @ColumnInfo(name = "name") var firstName: String,
    @ColumnInfo(name = "last_name") var lastName: String
)
Run Code Online (Sandbox Code Playgroud)

DAO 接口

interface MyModelDAO {
    @Insert
    fun createMyModel(myModel: MyModel)
}
Run Code Online (Sandbox Code Playgroud)

数据库

@Database(
    entities = [(MyModel::class)],
    version = 1,
    exportSchema = false
)
abstract class MyDb : RoomDatabase() {

    companion object {
        private var INSTANCE: MyDb? = null

        fun getInstance(context: Context): MyDb? {
            if (INSTANCE == null) {
                synchronized(MyDb::class) {
                    INSTANCE = Room.databaseBuilder(context.applicationContext,
                        MyDb::class.java, "mydb.db")
                        .allowMainThreadQueries()//for testing purposes only
                        .build()
                }
            }
            return INSTANCE
        }

        fun destroyInstance() {
            INSTANCE = null
        }
    }


    abstract fun getMyModelDao(): MyModelDAO
}
Run Code Online (Sandbox Code Playgroud)

这就是我尝试插入对象的方式。

val db = MinhaDb.getInstance(this)
db?.getMyModelDao()?.createMyModel(MyModel(111, "john", "doe"))
Run Code Online (Sandbox Code Playgroud)

问题是,该操作并未保留在 db 文件中。如果我进入databases文件夹,有一个mydb文件,一个wal和一个shm文件,并且没有在mydb. 但是,如果我db?.close()在插入操作之后调用,则操作会按预期进行(表已创建并填充)wal并且shm文件和文件不存在。

我在这里缺少什么?我很确定我不应该调用close()数据库。我试过用 abeginTransaction()和 aendTransaction()调用来包围插入调用,看看它是否改变了任何东西,但它没有。

更新: 正如@musooff 在评论中所解释的,显然这就是 sqlite dbs 的工作方式。我在插入调用之后查询了数据库,实际上,即使文件本身看起来是空的,也查询了返回的记录。

dgl*_*ano 10


TL; 博士

您的代码似乎工作正常。不要被 SQLite 创建的临时文件所迷惑。

  1. WAL 和 SHM 文件是您不必担心的临时内部文件。
  2. 如果您正在检查数据是否存在db,则直接检查文件,数据可能还不存在。等到您关闭连接。
  3. 使用 SQLiteBrowser 查看数据是否存在。您可以检查SQLiteBrowserAndroid 调试数据库
  4. 除了使用 SQLiteBrowser,您还可以使用 SELECT 查询简单地检查 Android 应用程序中是否存在数据。

WAL 和 SHM 文件

正如您所注意到的,在您的 db 目录中,您可以找到三个生成的文件:

your-database-name
your-database-name-shm
your-database-name-wal
Run Code Online (Sandbox Code Playgroud)

但是,对您而言,数据真正所在的唯一重要的是your-database-name.

wal文件用作Rollback Journal的替代品。

从版本 3.7.0 (2010-07-21) 开始,SQLite 支持一种称为“预写日志”或“WAL”的新事务控制机制。当数据库处于 WAL 模式时,与该数据库的所有连接都必须使用 WAL。特定数据库将使用回滚日志或 WAL,但不能同时使用两者。WAL 始终与数据库文件位于同一目录中,并与数据库文件具有相同的名称,但附加了字符串“-wal”。

您提到在wal文件仍然存在时无法看到数据库文件中的数据,然后关闭连接并且wal文件消失并且数据最终保留在数据库中,这是使用时的正确行为wal机制。

WAL 消失了

只要任何数据库连接打开了数据库,WAL 文件就会一直存在。通常,当最后一次与数据库的连接关闭时,会自动删除 WAL 文件。(更多在这里)

事务没有立即写入数据库文件

传统的回滚日志的工作原理是将原始未更改的数据库内容的副本写入单独的回滚日志文件,然后将更改直接写入数据库文件。在发生崩溃或 ROLLBACK 的情况下,包含在回滚日志中的原始内容将回放到数据库文件中,以将数据库文件恢复到其原始状态。COMMIT 在回滚日志被删除时发生。

WAL 方法颠倒了这一点。原始内容保留在数据库文件中,更改将附加到单独的 WAL 文件中。当一个表示提交的特殊记录被附加到 WAL 时,就会发生 COMMIT。因此 COMMIT 可以在不写入原始数据库的情况下发生,这允许读者在更改同时提交到 WAL 的同时从原始未更改的数据库继续操作。可以将多个事务附加到单个 WAL 文件的末尾。

当然,人们希望最终将附加在 WAL 文件中的所有事务传输回原始数据库。 将 WAL 文件事务移回数据库称为“检查点”。

默认情况下,当 WAL 文件达到 1000 页的阈值大小时,SQLite 会自动执行检查点。(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 编译时选项可用于指定不同的默认值。)使用 WAL 的应用程序无需执行任何操作即可发生这些检查点。但如果他们愿意,应用程序可以调整自动检查点阈值。或者他们可以关闭自动检查点并在空闲时刻或在单独的线程或进程中运行检查点。(更多在这里)

SHM只是暂时的共享内存文件,涉及到WAL机制,其唯一目的是:

共享内存文件不包含持久性内容。共享内存文件的唯一目的是提供一块共享内存,供以 WAL 模式访问同一数据库的多个进程使用。(更多在这里)