如何更改房间数据库的预期列顺序?预打包数据库出现无效模式错误。

huangapple go评论62阅读模式
英文:

How to change room db expected column order? Pre-packaged database has an invalid schema error

问题

我需要预填充数据库。我有两个问题,一个是:roomdb未导出模式,即使exportSchema设置为true。第二个是:room db重新排序列,并给我一个“预打包数据库具有无效模式”的错误。现在第一个问题可以忽略,但必须解决第二个问题才能继续。我不知道为什么会发生这种情况。我在SQLite浏览器中创建了数据库并看到了相同的问题。
错误信息:

期望:
TableInfo{name='words', columns={en=Column{name='en', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, pl=Column{name='pl', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, speech=Column{name='speech', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}}, foreignKeys=[], indices=[]}
发现:
TableInfo{name='words', columns={id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='undefined'}, en=Column{name='en', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, pl=Column{name='pl', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, speech=Column{name='speech', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}}, foreignKeys=[], indices=[]}

如上所示,“en”列和“id”列处于不同的位置。不确定但我怀疑这是错误的原因。

表创建语句:

CREATE TABLE words (id INTEGER PRIMARY KEY, en TEXT, pl TEXT, speech TEXT)

实体:

@Entity(tableName = "words")
data class MyEntity (
    @PrimaryKey
    @ColumnInfo(name = "id") val id: Int,
    @ColumnInfo(name = "en") val englishWord: String?,
    @ColumnInfo(name = "pl") val polishWord: String?,
    @ColumnInfo(name = "speech") val speech: String?
)
英文:

I need to prepopulate db. I have two problems, one is: roomdb not exporting schema, even though exportSchema is set to true. second is: room db reordering columns and giving me error of Pre-packaged database has an invalid schema. Now the first problem can be omitted, but second problem must be resolved to move forward. I have no idea why this is happening. I have created database in sqlite browser and saw the same issue.
The error is:

Expected:
TableInfo{name='words', columns={en=Column{name='en', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, pl=Column{name='pl', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, speech=Column{name='speech', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}}, foreignKeys=[], indices=[]}
 Found:
TableInfo{name='words', columns={id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='undefined'}, en=Column{name='en', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, pl=Column{name='pl', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}, speech=Column{name='speech', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='undefined'}}, foreignKeys=[], indices=[]}

as seen above the 'en' column and 'id' column in different position. Not sure but I suspect this is the reason of the error.

table creation statement:

CREATE TABLE words (id INTEGER PRIMARY KEY, en TEXT, pl TEXT, speech TEXT)

Entity:

@Entity(tableName = "words")
data class MyEntity (
    @PrimaryKey
    @ColumnInfo(name = "id")val id: Int,
    @ColumnInfo(name = "en") val englishWord: String?,
    @ColumnInfo(name = "pl") val polishWord: String?,
    @ColumnInfo(name = "speech") val speech: String?
)

答案1

得分: 1

如何更改 Room 数据库的期望列顺序?

列的顺序在“期望”(根据在相应的@Database注解类的entities参数中指定的@Entity注解类期望的模式)和“找到”(预填充的数据库)之间是无关紧要的。

重要的是“找到”表中的列定义与“期望”定义相匹配。

换句话说,在快速查看“期望”与“找到”之后,您会发现“id”列的期望具有“NOT NULL”约束,表创建语句(根据预填充的数据库)不包括“NOT NULL”约束。即

期望为 ==> id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}

找到为 ==> id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='undefined'}

要解决此问题,您可以执行以下操作:-

CREATE TABLE words (id INTEGER PRIMARY KEY NOT NULL, en TEXT, pl TEXT, speech TEXT)

比较并尝试确定如何解释期望/找到的差异并不是最简单的任务。始终确保 Room 期望的内容用于预填充数据库的模式要容易得多。这只需以下步骤:-

  1. 创建@Entity注解的类,并将它们包含在@Database类的entities参数中。

  2. 成功编译项目。

  3. 使用 Android Studio 的Android View,找到与@Database注解类同名但在java(generated)目录下带有_Impl后缀的类(通常至少有两个子目录)。

  4. 找到createAllTables方法,其中包含创建表的期望 SQL 的代码(如果让 Room 创建它们)。

    4.1 请注意,room_master_table无需创建,Room将根据需要创建和维护它。

您可能需要转换现有数据,可以在 SQLite 工具中轻松完成以下操作:-

  1. 重命名原始表

  2. 根据生成的 Java 中的 Room SQL 创建表(具有原始名称)

  3. 使用 SQL 从重命名的原始表复制数据

    3.1 在最简单的情况下,可以使用INSERT INTO the_table SELECT * FROM the_renamed_original_table;

  4. 删除重命名的原始表

替代方法

在您的情况下,替代方法可能是使用@ColumnInfo(name = "id")val id: Int?。但是,您可能希望参考 https://stackoverflow.com/questions/76008121/why-does-primarykey-val-id-int-null-works-when-creating-a-room-entity/76011195#76011195,因为在插入时使用可空 id 时 Room 会处理得与不同(即 0 被视为值 0 而不是生成的值,因此如果使用可能导致行未插入或在存在具有值 0 的另一行的情况下失败)。

  • 请注意,在某些情况下,您无法调整@Entity以适应预填充/现有模式。

替代方法的生成 Java 示例

考虑您的情况以及根据建议的可空 id 编写的代码:-

@Entity(tableName = "words")
data class MyEntity (
    @PrimaryKey
    @ColumnInfo(name = "id")val id: Int,
    @ColumnInfo(name = "en") val englishWord: String?,
    @ColumnInfo(name = "pl") val polishWord: String?,
    @ColumnInfo(name = "speech") val speech: String?
)

@Entity(tableName = "wordsV2")
data class MyEntityV2 (
    @PrimaryKey
    @ColumnInfo(name = "id")val id: Int?,
    @ColumnInfo(name = "en") val englishWord: String?,
    @ColumnInfo(name = "pl") val polishWord: String?,
    @ColumnInfo(name = "speech") val speech: String?
)

出于演示的目的,考虑以下内容:-

@Database(entities = [AutoGenTrueTable::class,AutoGenFalseTable::class,AutoGenFalseNullableTable::class,MyEntity::class,MyEntityV2::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() { ....
  • 仅关注MyEntityMyEntityV2(在演示中使用了现有项目)

然后找到生成的 Java:-

如何更改房间数据库的预期列顺序?预打包数据库出现无效模式错误。

  • 可以看到 words 表具有 NOT NULL 约束,wordsv2 没有,因此可能适用。

    • 有关 Room 处理插入(生成 id 列的值)的区别,请参阅上面的链接。
英文:

> How to change room db expected column order?

The order of the columns, between expected (the schema that Room expects according to the @Entity annotated classes that have been specified in the entities parameter of the respective @Database annotated class(es)) and the found (the pre-populated database) is irrelevant.

What matters is that the column definitions in the found table(s) match the expected definitions.

In other words, after having a quick look at the expected v found, you will see that the id column is expected to have a NOT NULL constraint, the table creation statement, as per the pre-populated database* does not include the NOT NULL constraint. i.e.

expected is ===> id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}

found is ==> id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='undefined'}

To overcome this issue then you could have:-

`CREATE TABLE words (id INTEGER PRIMARY KEY NOT NULL, en TEXT, pl TEXT, speech TEXT)`

Comparing and trying to ascertain how to interpret the expected/found discrepancies is not the easiest task. It is easier to always ensure that what Room expects is used for the schema of the pre-populated database. This is as simple as:-

  1. Creating the @Entity annotated class(es) and including them in the entities parameter of the @Database class(es).

  2. Successfully compiling the project.

  3. Using the Android Studio's Android View locating the class that is the same name as the @Database annotated class, but suffixed with _Impl in the java(generated) directories (there will typically be at least two sub directories)

  4. Locating the createAllTables method, in which the expected SQL for creating the tables (as Room would use if it was left to create them).

    4.1 Note that the room_master_table need not be created as Room will create and maintain this as required.

You may need to convert the existing data which can be done easily in an SQLite tool by:-

  1. Renaming the original table

  2. Creating the table as per the Room SQL from the generated java (this has the original name)

  3. Using SQL to copy the data from the renamed original

    3.1 This in the simplest scenario could be as simple as INSERT INTO the_table SELECT * FROM the_renamed_original_table;

  4. Dropping the renamed original table

Alternative Approach

In your case an alternative could be to use @ColumnInfo(name = "id")val id: Int?,. However, you may wish to refer to https://stackoverflow.com/questions/76008121/why-does-primarykey-val-id-int-null-works-when-creating-a-room-entity/76011195#76011195 as using a nullable id is treated differently by room when inserting (i.e. 0 is considered to be the value 0 rather than a generated value and thus if used could result in the row not being inserted or failing if another row exists with the value 0)

  • note that in some cases you cannot tailor the @Entity to suit a pre-populated/existing schema.

Example of Generated Java for the Alternative Approach

Consider your and the suggested nullable id as per:-

@Entity(tableName = "words")
data class MyEntity (
    @PrimaryKey
    @ColumnInfo(name = "id")val id: Int,
    @ColumnInfo(name = "en") val englishWord: String?,
    @ColumnInfo(name = "pl") val polishWord: String?,
    @ColumnInfo(name = "speech") val speech: String?
)

@Entity(tableName = "wordsV2")
data class MyEntityV2 (
    @PrimaryKey
    @ColumnInfo(name = "id")val id: Int?,
    @ColumnInfo(name = "en") val englishWord: String?,
    @ColumnInfo(name = "pl") val polishWord: String?,
    @ColumnInfo(name = "speech") val speech: String?
)

And for the sake of demonstrating the following:-

@Database(entities = [AutoGenTrueTable::class,AutoGenFalseTable::class,AutoGenFalseNullableTable::class,MyEntity::class,MyEntityV2::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() { ....
  • ignore all but MyEntity and MyEntityV2 (used an existing project for the demostration)

Then finding the generated java:-

如何更改房间数据库的预期列顺序?预打包数据库出现无效模式错误。

  • as can be seen the words table has the NOT NULL constraint, the wordsv2 does not, so may suit.

    • see link above re difference of how Room handles inserts (generation of the value for the id column).

huangapple
  • 本文由 发表于 2023年4月17日 08:08:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76030930.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定