Android Room仅在Lollipop 5.1 SDK 22上无效架构,具有重复的primaryKeyPosition

r1wp621o  于 2023-06-20  发布在  Android
关注(0)|答案(1)|浏览(122)

我有以下实体:

  1. @Entity(tableName = "game_regions",
  2. primaryKeys = {"_game", "_region_area"},
  3. foreignKeys = {
  4. @ForeignKey(
  5. entity = GameEntity.class,
  6. parentColumns = "_id",
  7. childColumns = "_game"
  8. ),
  9. @ForeignKey(
  10. entity = RegionAreaEntity.class,
  11. parentColumns = "_id",
  12. childColumns = "_region_area"
  13. )})
  14. public class GameRegionsEntity {
  15. @NonNull
  16. @ColumnInfo(name = "_game")
  17. private String game;
  18. @NonNull
  19. @ColumnInfo(name = "_region_area")
  20. private String regionArea;
  21. public GameRegionsEntity(@NonNull String game, @NonNull String regionArea) {
  22. this.game = game;
  23. this.regionArea = regionArea;
  24. }
  25. @NonNull
  26. public String getGame() {
  27. return game;
  28. }
  29. public void setGame(@NonNull String game) {
  30. this.game = game;
  31. }
  32. @NonNull
  33. public String getRegionArea() {
  34. return regionArea;
  35. }
  36. public void setRegionArea(@NonNull String regionArea) {
  37. this.regionArea = regionArea;
  38. }
  39. }

在SQL中以这种方式表示:

  1. CREATE TABLE "game_regions" (
  2. "_game" TEXT NOT NULL,
  3. "_region_area" TEXT NOT NULL,
  4. PRIMARY KEY("_game","_region_area"),
  5. FOREIGN KEY("_region_area") REFERENCES "region_areas"("_id"),
  6. UNIQUE("_game","_region_area"),
  7. FOREIGN KEY("_game") REFERENCES "games"("_id")
  8. );

当使用Android 6或更高版本的设备时,一切正常,没有任何错误,但是,当使用Android Lollipop 5.1时,它在架构首次验证时抛出以下异常:

  1. java.lang.IllegalStateException: Pre-packaged database has an invalid schema: game_regions(GameRegionsEntity).
  2. Expected:
  3. TableInfo{name='game_regions', columns={_region_area=Column{name='_region_area', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=2, defaultValue='undefined'}, _game=Column{name='_game', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}}, foreignKeys=[ForeignKey{referenceTable='games', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_game], referenceColumnNames=[_id]}, ForeignKey{referenceTable='region_areas', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_region_area], referenceColumnNames=[_id]}], indices=[]}
  4. Found:
  5. TableInfo{name='game_regions', columns={_game=Column{name='_game', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, _region_area=Column{name='_region_area', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}}, foreignKeys=[ForeignKey{referenceTable='region_areas', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_region_area], referenceColumnNames=[_id]}, ForeignKey{referenceTable='games', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_game], referenceColumnNames=[_id]}], indices=null}

如果我们比较EXPECTEDFOUND模式的结果:我们注意到唯一的变化是两个主键的primaryKeyPosition都是 * 1 *。
我不知道是什么导致了这个错误,为什么这是崩溃只在Android棒棒糖5.1(SDK 22)和它的工作在每个版本从23直到33.
有什么提示吗?

kt06eoxx

kt06eoxx1#

我建议不要为资产提供/使用您的SQL,而是使用房间创建的SQL。

  • (* 怀疑问题可能是UNIQUE索引**,因为这肯定与房间期望的不同***)
  • 据猜测,注解处理将UNIQUE索引的位置与主键位置混合,然后发出预期/找到的消息。
  • 不知道为什么它不抱怨后API 22.
    • 但是,始终建议使用room提供/生成的SQL为已有数据库创建SQLite组件。
  • 不需要UNIQUE索引,主键是隐式UNIQUE的。

Room SQL为 (来自测试):-

  1. CREATE TABLE IF NOT EXISTS `game_regions` (
  2. `_game` TEXT NOT NULL,
  3. `_region_area` TEXT NOT NULL,
  4. PRIMARY KEY(`_game`, `_region_area`),
  5. FOREIGN KEY(`_game`) REFERENCES `GameEntity`(`_id`) ON UPDATE NO ACTION ON DELETE NO ACTION ,
  6. FOREIGN KEY(`_region_area`) REFERENCES `RegionAreaEntity`(`_id`) ON UPDATE NO ACTION ON DELETE NO ACTION
  7. )

1.您可能希望在您最喜欢的SQLite工具中,通过以下方式转换现有的资产数据库:

  1. 1.重命名game_regions
  • 1.使用上面的SQL(或者更好的是Room生成的SQL)
  • 在Android视图中,查找Java(生成的)目录,然后
  • 打开与注解为@Database但后缀为**_Impl**的类同名的类
  • 找到**createAllTables**方法,该方法包含预期表的实际SQL (注意,Room注解不直接支持UNIQUE约束)
  1. 1.将数据从重命名的原始表复制到新创建的表,例如INSERT INTO game_regions SELECT * FROM game_regions_renamed***(未测试)***
  2. 1.删除原games_regions表。
  3. 1.可能/可选地在已更改的数据库上运行VACUUM。
  4. 1.保存数据库。
    • 测试**(也称为建议答案的原因)

最初,使用您的代码加上以下支持代码(以模仿您未提供的代码)创建的项目:

    • 游戏实体类**
  1. @Entity
  2. class GameEntity {
  3. @NonNull
  4. @PrimaryKey
  5. String _id;
  6. String game_type;
  7. }
    • RegionAreaEntity类**
  1. @Entity
  2. class RegionAreaEntity {
  3. @NonNull
  4. @PrimaryKey
  5. String _id;
  6. }
    • Database抽象类**(即@Database注解类)
  1. @Database(entities = {GameEntity.class,RegionAreaEntity.class,GameRegionsEntity.class},exportSchema = false,version = 1)
  2. abstract class TheDatabase extends RoomDatabase {
  3. private static volatile TheDatabase instance;
  4. public static TheDatabase getInstance(Context context) {
  5. if (instance==null) {
  6. instance = Room.databaseBuilder(
  7. context,TheDatabase.class,"the_database"
  8. )
  9. .allowMainThreadQueries()
  10. .createFromAsset("the_database.db")
  11. .build();
  12. }
  13. return instance;
  14. }
  15. }
  • 请注意,. allowMainThreadQueries适合在主线程上运行(为了简单和简洁)。
    • 主要活动**(待测)
  1. public class MainActivity extends AppCompatActivity {
  2. TheDatabase db;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. db = TheDatabase.getInstance(this);
  8. SupportSQLiteDatabase suppdb = db.getOpenHelper().getWritableDatabase();
  9. }
  10. }
  • 即,这将强制打开数据库。
    • 测试部分1**

createFromAsset方法调用已删除,并在API 22设备(Android Studio中的模拟器)上运行。它运行良好。因此消除了API 22明显与Room不兼容的任何问题。

    • 测试部分2**

应用程序被卸载,createFromAsset被恢复。使用Navicat(一个SQLite工具)创建了数据库,其中包含两个支持表(GameEntity和RegionAreaEnntity,使用由room生成的SQL):-

  1. DROP TABLE IF EXISTS game_regions;
  2. DROP TABLE IF EXISTS gameentity;
  3. DROP TABLE IF EXISTS regionareaentity;
  4. CREATE TABLE IF NOT EXISTS `GameEntity` (`_id` TEXT NOT NULL, `game_type` TEXT, PRIMARY KEY(`_id`));
  5. CREATE TABLE IF NOT EXISTS `RegionAreaEntity` (`_id` TEXT NOT NULL, PRIMARY KEY(`_id`));
  6. CREATE TABLE "game_regions" (
  7. "_game" TEXT NOT NULL,
  8. "_region_area" TEXT NOT NULL,
  9. PRIMARY KEY("_game","_region_area"),
  10. FOREIGN KEY("_region_area") REFERENCES "region_areas"("_id"),
  11. UNIQUE("_game","_region_area"),
  12. FOREIGN KEY("_game") REFERENCES "games"("_id")
  13. );

运行SQL:-

  1. DROP TABLE IF EXISTS regionareaentity
  2. > OK
  3. > Time: 0.024s
  4. CREATE TABLE IF NOT EXISTS `GameEntity` (`_id` TEXT NOT NULL, `game_type` TEXT, PRIMARY KEY(`_id`))
  5. > OK
  6. > Time: 0.024s
  7. CREATE TABLE IF NOT EXISTS `RegionAreaEntity` (`_id` TEXT NOT NULL, PRIMARY KEY(`_id`))
  8. > OK
  9. > Time: 0.028s
  10. CREATE TABLE "game_regions" (
  11. "_game" TEXT NOT NULL,
  12. "_region_area" TEXT NOT NULL,
  13. PRIMARY KEY("_game","_region_area"),
  14. FOREIGN KEY("_region_area") REFERENCES "region_areas"("_id"),
  15. UNIQUE("_game","_region_area"),
  16. FOREIGN KEY("_game") REFERENCES "games"("_id")
  17. )
  18. > OK
  19. > Time: 0.024s

已储存资料库(关闭资料库及连线,然后退出Navicat)。已将文件复制到assets文件夹(将文件重命名为_database. db)。运行应用程序并:-

  1. 2023-06-16 11:07:20.700 4744-4744/a.a.so76483436javaroomapi22issue E/AndroidRuntime: FATAL EXCEPTION: main
  2. Process: a.a.so76483436javaroomapi22issue, PID: 4744
  3. java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so76483436javaroomapi22issue/a.a.so76483436javaroomapi22issue.MainActivity}: java.lang.IllegalStateException: Pre-packaged database has an invalid schema: game_regions(a.a.so76483436javaroomapi22issue.GameRegionsEntity).
  4. Expected:
  5. TableInfo{name='game_regions', columns={_region_area=Column{name='_region_area', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=2, defaultValue='undefined'}, _game=Column{name='_game', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}}, foreignKeys=[ForeignKey{referenceTable='RegionAreaEntity', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_region_area], referenceColumnNames=[_id]}, ForeignKey{referenceTable='GameEntity', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_game], referenceColumnNames=[_id]}], indices=[]}
  6. Found:
  7. TableInfo{name='game_regions', columns={_game=Column{name='_game', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='undefined'}, _region_area=Column{name='_region_area', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=2, defaultValue='undefined'}}, foreignKeys=[ForeignKey{referenceTable='games', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_game], referenceColumnNames=[_id]}, ForeignKey{referenceTable='region_areas', onDelete='NO ACTION +', onUpdate='NO ACTION', columnNames=[_region_area], referenceColumnNames=[_id]}], indices=null}
  8. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
  • 当pkey位置颠倒时,结果略有不同(不确定确切原因,但怀疑是由于项目不同)。
    • 测试部分3**(使用Room的SQL)

已卸载应用程序。在Navicat(Room的SQL)中使用以下命令:-

  1. DROP TABLE IF EXISTS game_regions;
  2. DROP TABLE IF EXISTS gameentity;
  3. DROP TABLE IF EXISTS regionareaentity;
  4. CREATE TABLE IF NOT EXISTS `GameEntity` (`_id` TEXT NOT NULL, `game_type` TEXT, PRIMARY KEY(`_id`));
  5. CREATE TABLE IF NOT EXISTS `RegionAreaEntity` (`_id` TEXT NOT NULL, PRIMARY KEY(`_id`));
  6. CREATE TABLE IF NOT EXISTS `game_regions` (`_game` TEXT NOT NULL, `_region_area` TEXT NOT NULL, PRIMARY KEY(`_game`, `_region_area`), FOREIGN KEY(`_game`) REFERENCES `GameEntity`(`_id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`_region_area`) REFERENCES `RegionAreaEntity`(`_id`) ON UPDATE NO ACTION ON DELETE NO ACTION );
  7. /*
  8. CREATE TABLE "game_regions" (
  9. "_game" TEXT NOT NULL,
  10. "_region_area" TEXT NOT NULL,
  11. PRIMARY KEY("_game","_region_area"),
  12. FOREIGN KEY("_region_area") REFERENCES "region_areas"("_id"),
  13. UNIQUE("_game","_region_area"),
  14. FOREIGN KEY("_game") REFERENCES "games"("_id")
  15. );
  16. */

导致:-

  1. DROP TABLE IF EXISTS game_regions
  2. > OK
  3. > Time: 0.517s
  4. DROP TABLE IF EXISTS gameentity
  5. > OK
  6. > Time: 0.024s
  7. DROP TABLE IF EXISTS regionareaentity
  8. > OK
  9. > Time: 0.024s
  10. CREATE TABLE IF NOT EXISTS `GameEntity` (`_id` TEXT NOT NULL, `game_type` TEXT, PRIMARY KEY(`_id`))
  11. > OK
  12. > Time: 0.024s
  13. CREATE TABLE IF NOT EXISTS `RegionAreaEntity` (`_id` TEXT NOT NULL, PRIMARY KEY(`_id`))
  14. > OK
  15. > Time: 0.024s
  16. CREATE TABLE IF NOT EXISTS `game_regions` (`_game` TEXT NOT NULL, `_region_area` TEXT NOT NULL, PRIMARY KEY(`_game`, `_region_area`), FOREIGN KEY(`_game`) REFERENCES `GameEntity`(`_id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`_region_area`) REFERENCES `RegionAreaEntity`(`_id`) ON UPDATE NO ACTION ON DELETE NO ACTION )
  17. > OK
  18. > Time: 0.024s
  19. /*
  20. CREATE TABLE "game_regions" (
  21. "_game" TEXT NOT NULL,
  22. "_region_area" TEXT NOT NULL,
  23. PRIMARY KEY("_game","_region_area"),
  24. FOREIGN KEY("_region_area") REFERENCES "region_areas"("_id"),
  25. UNIQUE("_game","_region_area"),
  26. FOREIGN KEY("_game") REFERENCES "games"("_id")
  27. );
  28. */
  29. > not an error
  30. > Time: 0s

关闭数据库和连接并退出Navicat。已将数据库复制到assets目录(重命名以前使用的db文件)。运行了应用程序,运行正常。设备浏览器显示(应用程序检查仅适用于API 26+):-

最后,显示生成的java(以及资产):

展开查看全部

相关问题