房间没有更新数据库的架构

Posted

技术标签:

【中文标题】房间没有更新数据库的架构【英文标题】:Room is not updating schema of the database 【发布时间】:2022-01-23 20:02:55 【问题描述】:

我有下表:

@JsonClass(generateAdapter = true)
@Entity(tableName = "lexeme")
data class Lexeme(
    @PrimaryKey
    @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
    val id: Long,
    val form: String,
    val formNoAccent: String,
    val formUtf8General: String,
    val reverse: String,
    val number: Int?,
    val description: String?,
    val noAccent: String?,
    val consistentAccent: String?,
    val frequency: String?,
    val hyphenations: String?,
    val pronunciations: String?,
    val stopWord: String?,
    val compound: String?,
    val modelType: String?,
    val modelNumber: String?,
    val restriction: String?,
    val staleParadigm: String?,
    val notes: String?,
    val hasApheresis: String?,
    val hasApocope: String?,
    val createDate: String?,
    val modDate: String?
)

无论我想做什么,idformformNoAccentformUtf8General 都没有设置为 Not Nullid 没有设置为 affinity = 3,我都尝试过 Int和长。

我正在尝试将数据库预加载到我的应用程序中,但出现以下错误:

     Expected:
    TableInfoname='lexeme', columns=hyphenations=Columnname='hyphenations', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', staleParadigm=Columnname='staleParadigm', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', modDate=Columnname='modDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', notes=Columnname='notes', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', hasApheresis=Columnname='hasApheresis', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', description=Columnname='description', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', consistentAccent=Columnname='consistentAccent', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', formNoAccent=Columnname='formNoAccent', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null', noAccent=Columnname='noAccent', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', modelType=Columnname='modelType', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', reverse=Columnname='reverse', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null', compound=Columnname='compound', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', hasApocope=Columnname='hasApocope', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', frequency=Columnname='frequency', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', number=Columnname='number', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null', formUtf8General=Columnname='formUtf8General', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null', form=Columnname='form', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null', restriction=Columnname='restriction', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', modelNumber=Columnname='modelNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', id=Columnname='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null', stopWord=Columnname='stopWord', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', pronunciations=Columnname='pronunciations', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', createDate=Columnname='createDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', foreignKeys=[], indices=[]
     Found:
TableInfoname='lexeme', columns=hyphenations=Columnname='hyphenations', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', staleParadigm=Columnname='staleParadigm', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', modDate=Columnname='modDate', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', notes=Columnname='notes', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', hasApheresis=Columnname='hasApheresis', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', description=Columnname='description', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', consistentAccent=Columnname='consistentAccent', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', formNoAccent=Columnname='formNoAccent', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', noAccent=Columnname='noAccent', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', modelType=Columnname='modelType', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', reverse=Columnname='reverse', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', compound=Columnname='compound', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', hasApocope=Columnname='hasApocope', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', frequency=Columnname='frequency', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', number=Columnname='number', type='integer', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null', formUtf8General=Columnname='formUtf8General', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', form=Columnname='form', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', restriction=Columnname='restriction', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', modelNumber=Columnname='modelNumber', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', id=Columnname='id', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', stopWord=Columnname='stopWord', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', pronunciations=Columnname='pronunciations', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', createDate=Columnname='createDate', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', foreignKeys=[], indices=[]

【问题讨论】:

【参考方案1】:

确保 found 匹配 expected 的最简单方法是使用 Room 生成的 SQL 在工具中创建表用于创建预打包的数据库。

创建所有实体后编译项目,并将它们添加到使用@Database注解的类的实体列表中,可以找到该SQL。

编译后查看java(生成的),找到与@Database注解的类同名,后缀为_Impl。在该类中会有一个名为 createAllTables 的方法,它包含所有表、视图、索引的 SQL,以及房间期望的触发器的 FTS 表。

例如:-

修复

您必须确保预打包数据库中的表(找到)与 SQL 中的表(预期)匹配。

假设您已经发现了所有差异,那么您可以尝试将 Lexeme 类更改为:-

@JsonClass(generateAdapter = true)
@Entity(tableName = "lexeme")
data class Lexeme(
    @PrimaryKey
    //@ColumnInfo(typeAffinity = ColumnInfo.INTEGER) //<<<<< effectively removed 
    val id: String, //<<<<< changed to be TEXT
    val form: String?, //<<<<< changes to be NOT NULL
    val formNoAccent: String?, //<<<<< changes to be NOT NULL 
    val formUtf8General: String?, //<<<<< changes to be NOT NULL
    val reverse: String, //<<<<< changes to be NOT NULL (not found by yourself)
    val number: Int?,
    val description: String?,
    val noAccent: String?,
    val consistentAccent: String?,
    val frequency: String?,
    val hyphenations: String?,
    val pronunciations: String?,
    val stopWord: String?,
    val compound: String?,
    val modelType: String?,
    val modelNumber: String?,
    val restriction: String?,
    val staleParadigm: String?,
    val notes: String?,
    val hasApheresis: String?,
    val hasApocope: String?,
    val createDate: String?,
    val modDate: String?
)
请注意,我没有尝试上述方法(在某种程度上有但仍不能保证我发现了所有的不匹配) 您可能需要在其他代码中进行其他更改以适应,请特别注意 id 列,因为不会像预期的那样生成 id。 我个人不建议尝试将实体(预期)与现有表(找到)匹配。 所以我建议考虑以下因素来调整预打包的数据库以适应实体:-

以上修改后生成的SQL是:-

CREATE TABLE IF NOT EXISTS `lexeme` (
    `id` TEXT NOT NULL, 
    `form` TEXT, 
    `formNoAccent` TEXT, 
    `formUtf8General` TEXT, 
    `reverse` TEXT NOT NULL, /* <<<<< ANOTHER MISMATCH SPOTTED */
    `number` INTEGER, 
    `description` TEXT, 
    `noAccent` TEXT, 
    `consistentAccent` TEXT, 
    `frequency` TEXT, 
    `hyphenations` TEXT, 
    `pronunciations` TEXT, 
    `stopWord` TEXT, 
    `compound` TEXT, 
    `modelType` TEXT, 
    `modelNumber` TEXT, 
    `restriction` TEXT, 
    `staleParadigm` TEXT, 
    `notes` TEXT, 
    `hasApheresis` TEXT, 
    `hasApocope` TEXT, 
    `createDate` TEXT, 
    `modDate` TEXT, 
    PRIMARY KEY(`id`)
    )
注意使用上述内容,因为 reverse 列是奇数列,检查找到的并发现它也不匹配(因此更改)。因此,为什么我建议不要尝试将 Entity 与预打包匹配,而是使用 Room 生成的 SQL 来创建/修改预打包。

如果您在预打包的数据库中有大量数据,那么您可以使用 SQL 来(如果您没有数据或数据很少,则只需删除词素表并按照第 2 步创建):-

    重命名词位表,例如

    ALTER TABLE lexeme RENAME TO lexeme_old;

    使用从生成的 java 复制的词位表的 SQL 创建词位。

    将数据从 lexeme_old 表复制到 lexeme 表,例如

    INSERT INTO lexeme (SELECT * FROM lexeme_old);

    使用 DROP TABLE IF EXISTS lexeme_old 删除 lexeme_old 表

    可选择使用 SQL VACUUM;

    关闭数据库,退出工具,启动工具,打开/连接数据库检查更改是否已应用,关闭数据库然后将预打包的数据库复制到应用程序。

请注意,由于 id 列是 TEXT,因此如果任何值不是有效的整数,那么您将收到 SQLITE_MISMATCH 错误。

请注意,以上假设没有其他实体/表或触发器会使事情复杂化(特别是如果 Lexeme 表是其他表的父级)。

注意,上述方法不一定有效(如果您查看下面链接的示例,它会删除索引、触发器和视图,并且在复制数据之前不会创建它们)

如果您确实获得了 SQLITE_MISMATCH,那么您可以执行以下操作之一:-

    将 id 列从 val id: Long 更改为 val id: String 并删除行 @ColumnInfo(typeAffinity = ColumnInfo.INTEGER)。 将步骤 3 中使用的 SQL 更改为

:-

INSERT INTO lexeme 
    (
        `form`,
        `formNoAccent`,
        `formUtf8General`,
        `reverse`,
        `number`,
        `description`,
        `noAccent`,
        `consistentAccent`,
        `frequency`,
        `hyphenations`,
        `pronunciations`,
        `stopWord`,
        `compound`,
        `modelType`,
        `modelNumber`,
        `restriction`,
        `staleParadigm`,
        `notes`,
        `hasApheresis`,
        `hasApocope`,
        `createDate`,
        `modDate`
    ) 
    SELECT 
        `form`,
        `formNoAccent`,
        `formUtf8General`,
        `reverse`,
        `number`,
        `description`,
        `noAccent`,
        `consistentAccent`,
        `frequency`,
        `hyphenations`,
        `pronunciations`,
        `stopWord`,
        `compound`,
        `modelType`,
        `modelNumber`,
        `restriction`,
        `staleParadigm`,
        `notes`,
        `hasApheresis`,
        `hasApocope`,
        `createDate`,
        `modDate` 
    FROM lexeme_old
;
注意 SQL 没有被检查,认为它在原则上。

上述步骤可以在您使用/确实用于创建预打包数据库的任何工具中进行。

另一种选择是使用prePackagedDarabaseCallback 即时进行更改。 This is an example of using the callback 实际上只需进行一些更改即可完成上述步骤。但是,它是用 java 编写的,并且至少需要 Room 版本 2.4.0-beta02(建议使用 2.4.0,因为它现在已经发布)。

【讨论】:

以上是关于房间没有更新数据库的架构的主要内容,如果未能解决你的问题,请参考以下文章

我的RESTful API没有更新数据库,但是ajax返回成功

如何更新云套件数据

将回收站视图数据中的 onMove() 更改更新到房间数据库

(Android) 房间数据库迁移不会改变数据库

连接值更改时 Firebase 数据未更新

SQL - 触发更新错误