在房间数据库中调用 'insert' 未完成交易

Calling 'insert' in a room database does not complete the transaction

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

模特class

@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
)

DAO 接口

interface MyModelDAO {
    @Insert
    fun createMyModel(myModel: MyModel)
}

数据库

@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
}

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

val db = MinhaDb.getInstance(this)
db?.getMyModelDao()?.createMyModel(MyModel(111, "john", "doe"))

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

我在这里错过了什么?我很确定我不应该在数据库上调用 close() 。我试过用 beginTransaction()endTransaction() 调用包围插入调用,看看它是否改变了什么,但它没有。

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


TL;DR

您的代码似乎运行良好。不要被 SQLite 创建用于操作的临时文件搞糊涂了。

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

WAL 和 SHM 文件

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

your-database-name
your-database-name-shm
your-database-name-wal

但是,对您来说唯一重要的是数据真正存在的地方 your-database-name

wal 文件用作 Rollback Journal 的替换。

Beginning with version 3.7.0 (2010-07-21), SQLite supports a new transaction control mechanism called "write-ahead log" or "WAL". When a database is in WAL mode, all connections to that database must use the WAL. A particular database will use either a rollback journal or a WAL, but not both at the same time. The WAL is always located in the same directory as the database file and has the same name as the database file but with the string "-wal" appended.

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

WAL消失

The WAL file exists for as long as any database connection has the database open. Usually, the WAL file is deleted automatically when the last connection to the database closes. (More here)

交易没有立即写入数据库文件

The traditional rollback journal works by writing a copy of the original unchanged database content into a separate rollback journal file and then writing changes directly into the database file. In the event of a crash or ROLLBACK, the original content contained in the rollback journal is played back into the database file to revert the database file to its original state. The COMMIT occurs when the rollback journal is deleted.

The WAL approach inverts this. The original content is preserved in the database file and the changes are appended into a separate WAL file. A COMMIT occurs when a special record indicating a commit is appended to the WAL. Thus a COMMIT can happen without ever writing to the original database, which allows readers to continue operating from the original unaltered database while changes are simultaneously being committed into the WAL. Multiple transactions can be appended to the end of a single WAL file.

Of course, one wants to eventually transfer all the transactions that are appended in the WAL file back into the original database. Moving the WAL file transactions back into the database is called a "checkpoint".

By default, SQLite does a checkpoint automatically when the WAL file reaches a threshold size of 1000 pages. (The SQLITE_DEFAULT_WAL_AUTOCHECKPOINT compile-time option can be used to specify a different default.) Applications using WAL do not have to do anything in order to for these checkpoints to occur. But if they want to, applications can adjust the automatic checkpoint threshold. Or they can turn off the automatic checkpoints and run checkpoints during idle moments or in a separate thread or process. (More here)

SHM只是一个临时共享内存文件,与WAL机制有关,其唯一用途是:

The shared-memory file contains no persistent content. The only purpose of the shared-memory file is to provide a block of shared memory for use by multiple processes all accessing the same database in WAL mode. (More here)