Room 没有更新数据库的架构

Room is not updating schema of the database

我有以下 table:

@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和Long我都试过了

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

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

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

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

编译后查看java(生成的),找到与class同名的class,用@Database注解,后缀为_Impl。在 class 中将有一个名为 createAllTables 的方法,它具有 SQL 用于所有 table、视图、索引和 FTS tables 房间期望的触发器。

  • 例如:-

修复

您必须确保预打包数据库中的table(找到)与SQL(预计).

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

@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。
  • 我个人不建议尝试将实体(预期)与现有的table(已找到 ). 所以我建议考虑以下内容来调整预打包的数据库以适应实体:-

经过以上修改后生成的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 列是奇数列,检查发现发现它也不匹配(因此更改)。因此,为什么我建议不要尝试将实体与预打包相匹配,而是使用 Room 生成的 SQL 来 create/amend 预打包。

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

  1. 重命名词位 table 例如

    ALTER TABLE lexeme RENAME TO lexeme_old;

  2. 使用 SQL 为从生成的 java.

    复制的词素 table 创建词素
  3. 将数据从 lexeme_old table 复制到词位 table 例如

    INSERT INTO lexeme (SELECT * FROM lexeme_old);

  4. 删除 lexeme_old table 使用 DROP TABLE IF EXISTS lexeme_old

  5. 可选择使用 SQL VACUUM;

  6. 关闭数据库,退出工具,启动工具,open/connect数据库检查是否应用了更改,关闭数据库然后将预打包的数据库复制到应用程序.

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

  • 注意上面假设没有其他 entities/tables 或触发器可能会使事情复杂化(特别是如果 Lexeme tables 是其他 tables).

  • 注意,上面的不一定是有效的 (如果你看下面链接的例子,它会删除索引、触发器和视图,并且在数据完成之前不会创建它们已复制)

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

  1. 将 id 列从 val id: Long 更改为 val id: String 并删除行 @ColumnInfo(typeAffinity = ColumnInfo.INTEGER).
  2. 将步骤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还没有检查,原则上是这样。

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

另一种选择是使用 prePackagedDarabaseCallback 即时进行更改。 只需稍作改动即可执行上述步骤。但是,它是用 java 编写的,它还至少需要 Room 版本 2.4.0-beta02(建议使用 2.4.0,因为它现已发布)。