【问题标题】:Room Pre-packaged database has an invalid schema ERROR of Expect type=Integer Found=Boolean房间预打包的数据库有一个无效的架构错误期望类型=整数找到=布尔
【发布时间】:2022-01-29 04:25:49
【问题描述】:

我对这个问题有点迷茫。

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 Room @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 迁移单个列以查看它是否匹配预期/找到的输出。

        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 数据库,哪个是资产数据库。我的预感是“找到”输出是与资产匹配的输出,因为BOOLEAN 类型,但这并不完全清楚。为什么将 Room @ColumnInfo 值类型从 Boolean 更改为 Int 似乎没有生效?如果需要为我的数据库上BOOLEAN 类型的每一列实施上述迁移,那么对多个表应用迁移的正确方法是什么? (因为我有更多具有这种BOOLEAN 类型的表,尽管它们没有出现错误)

【问题讨论】:

    标签: kotlin android-room android-room-prepackageddatabase


    【解决方案1】:

    为什么将 Room @ColumnInfo 值类型从 Boolean 更改为 Int 似乎没有生效?

    因为对于 Room,它们与任何类型 Long/long 的类型相同 -> 整数(与十进制相对)布尔值在创建表 SQL 中被赋予 INTEGER 列类型。

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

    然而,SQLite 非常灵活,您可以创建几乎任何类型的列(只要该类型不破坏解析器规则,例如作为关键字)。然后,SQLite 使用一组规则来分配类型亲和性(上面列出的 4 个之一或包罗万象的 NUMERIC(哪个房间不支持/允许))。

    所以从上面 问题在于预打包的数据库。也就是说,预打包的数据库有一个用 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注解类后编译项目,则可以从room获取CREATE TABLE SQL。
    • 编译成功后,Room 将在类中生成一些 java 代码,其名称与 @Database 注释类相同,但后缀为 _Impl。在这个类中将有一个方法 createAllTables 用于创建表(索引和视图)的 SQL。这个 SQL 是房间所期望的

    您可以在多个地方更改表格,最简单的是:-

    1. 在预打包的数据库中进行更改(如上),然后将更改的文件复制到 assets 文件夹中,或者
    2. 使用 prePackagedDatabaseCallback 回调请参阅How do I use Room's prepackagedDatabaseCallback? 2. 复制文件后,验证(Expected v Found)执行之前调用回调。

    【讨论】:

    • 必须从您的列表中选择第一个解决方案并直接对预打包的数据库进行更改,谢谢。 Althoug “因为对于 Room,它们与任何类型 Long/long -> Boolean that is an integer 的类型相同,这是有道理的,感谢您的解释,我不明白为什么 sqlite 没有将BOOLEAN 转换为INTEGER 也不会在CREATE TABLE 语句中发出警告,因此预打包的数据类型也是INTEGER。不应该做吗?
    • @JesusJimenez SQLite 没有 BOOLEAN 列的概念,类型明智的是 NUMERIC,如果 0 或 1 的实际数据将存储为 0 或 1 字节。但是,对房间来说重要的是架构(用于创建表的 SQL),因此即使 INT INTEGER BIGINT ANYTHINGWITHINT 都具有 INTEGER 的类型亲和性(请参阅sqlite.org/datatype3.html),Room 只会接受 INTEGER 而不是什么。所以产生的类型亲和力是无关紧要的。
    猜你喜欢
    • 1970-01-01
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多