Room 预打包数据库有一个无效的模式 ERROR of Expect type=Integer Found=Boolean

Room Pre-packaged database has an invalid schema ERROR of Expect type=Integer Found=Boolean

我对这个问题有点迷茫。

Pre-packaged database has an invalid schema 错误输出如下:

预计

TableInfo{name='account', columns={client_alt_phone_on_route_sheets=Column{name='client_alt_phone_on_route_sheets', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, client_titles_on_address_labels=Column{name='client_titles_on_address_labels', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='0'}, client_titles_on_invoices=Column{name='client_titles_on_invoices', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='0'}}, foreignKeys=[], indices=[]}

找到

TableInfo{name='account', columns={client_alt_phone_on_route_sheets=Column{name='client_alt_phone_on_route_sheets', type='BOOLEAN', affinity='1', notNull=true, primaryKeyPosition=0, defaultValue='FALSE'}, client_titles_on_address_labels=Column{name='client_titles_on_address_labels', type='BOOLEAN', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='FALSE'}, client_titles_on_invoices=Column{name='client_titles_on_invoices', type='BOOLEAN', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='FALSE'}}, foreignKeys=[], indices=[]}

我省略了其他一些列,因为它们不是问题所在并且它们的输出匹配。问题出在预期 INTEGER 但发现 BOOLEAN.

的列

数据库架构如下:

CREATE TABLE account
(
    client_alt_phone_on_route_sheets  BOOLEAN DEFAULT FALSE NOT NULL,
    client_titles_on_address_labels   BOOLEAN DEFAULT FALSE,
    client_titles_on_invoices         BOOLEAN DEFAULT FALSE,
    // Omitted rows
);

最初,我确实创建了 account 房间 @Entity,其中 BOOLEAN 列的类型为 Boolean:

@Entity(tableName = "account")
data class Account(
    // Omitted data
    // @FIXME
    @ColumnInfo(name = "client_alt_phone_on_route_sheets", defaultValue = "FALSE") val routeSheetsClientAltPhone: Boolean,

    // @FIXME
    @ColumnInfo(name = "client_titles_on_address_labels", defaultValue = "FALSE") val clientTitlesOnAddressLabels: Boolean?,

    // @FIXME
    @ColumnInfo(name = "client_titles_on_invoices", defaultValue = "FALSE") val clientTitlesOnInvoices: Boolean?,
)

然后,第一次抛出错误时,我确实将 Boolean 类型列更改为 Int 类型:

@Entity(tableName = "account")
data class Account(
    // Omitted data
    // @FIXME
    @ColumnInfo(name = "client_alt_phone_on_route_sheets", defaultValue = "0") val routeSheetsClientAltPhone: Int,

    // @FIXME
    @ColumnInfo(name = "client_titles_on_address_labels", defaultValue = "0") val clientTitlesOnAddressLabels: Int?,

    // @FIXME
    @ColumnInfo(name = "client_titles_on_invoices", defaultValue = "0") val clientTitlesOnInvoices: Int?,
)

但是,错误不断发生。我尝试使用 this answer 迁移单个列以查看它是否在 expected/found 输出中匹配。

        private val MIGRATION_1_2 = object : Migration(1,2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL(
                    "ALTER TABLE account ADD COLUMN client_alt_phone_on_route_sheets INTEGER NOT NULL DEFAULT(0)"
                )
            }
        }

        private fun buildDatabase(context: Context) = Room.databaseBuilder(
            context.applicationContext,
            Database::class.java,
            Constants.DATABASE_NAME
        ).addMigrations(MIGRATION_1_2).createFromAsset("database.db").build()

但显然迁移没有成功,也没有改变输出中的任何内容,因为错误仍然存​​在。另外,我不确定哪个是 Room 数据库,哪个是 assets 数据库。我的直觉是 'Found' 输出是与资产匹配的输出,因为 BOOLEAN 类型,但这并不完全清楚。为什么将 Room @ColumnInfo 值类型从 Boolean 更改为 Int 似乎没有生效?如果上面的迁移需要对我的数据库中 BOOLEAN 类型的每一列实施,那么对多个表应用迁移的正确方法是什么? (因为我有更多具有此 BOOLEAN 类型的表,尽管它们不在错误中)

Why does it seems like changing the Room @ColumnInfo value type from Boolean to Int doesn't seem to take effect?

因为对于 Room 它们是任何类型的相同类型 Long/long -> 整数布尔值(与十进制相反)在创建时被赋予了 INTEGER 列类型 table SQL.

Room 只会创建 INTEGER、TEXT、REAL 或 BLOB 类型的列。

然而,

SQLite 非常灵活,您可以创建几乎任何类型的列(只要该类型不违反解析器规则,例如作为关键字)。 SQLite 然后使用一组规则来分配类型亲和力(上面列出的 4 个之一或 catch-all NUMERIC(哪个房间没有 support/allow))。

所以从上面 问题出在 pre-packaged 数据库 上。那就是 pre-packaged 数据库有一个用 BOOLEAN room expects INTEGER 定义的列,因此您必须将列类型从 BOOLEAN 更改为 INTEGER。

您可以使用以下方式进行此更改:-

ALTER TABLE account RENAME TO account_old;
CREATE TABLE IF NOT EXISTS account (client_alt_phone_on_route_sheets INTEGER NOT NULL DEFAULT VALUE false,  .....);
INSERT INTO account SELECT * FROM account_old;
DROP TABLE IF EXISTS account_old;
  • 注意 如果在创建实体和@Database 注释后编译项目,您可以从房间获得 CREATE TABLE SQL class 与实体列表中定义的实体。
  • 编译成功后,Room 将在 class 中生成一些 java 代码,其名称与 @Database 相同,注释为 class 但后缀为 _Impl。在此 class 中将有一个方法 createAllTables SQL 用于创建 tables(索引和视图)。这个 SQL 是房间 期望的

您可以在不同的地方更改 table(s),最简单的是:-

  1. 在pre-packaged数据库中进行更改(如上),然后将更改后的文件复制到资产文件夹中,或者
  2. 使用 prePackagedDatabaseCallback 回调见 2. 在复制文件之后但在执行验证(预期 v 找到)之前调用回调。