会议室数据库迁移无法正确处理ALTER TABLE迁移

时间:2017-09-22 19:34:32

标签: android sqlite android-room

  

Java.lang.IllegalStateException

     

迁移没有得到妥善处理   用户(therealandroid.github.com.roomcore.java.User)。

     

预期:

     

TableInfo {name =' user',columns = {name = Column {name =' name',type =' TEXT',   notNull = false,primaryKeyPosition = 0},age =列{name =' age',   type =' INTEGER',notNull = true,primaryKeyPosition = 0},   id =列{name =' id',type =' INTEGER',notNull = true,   primaryKeyPosition = 1}},foreignKeys = []}找到:

     

实测值

     

TableInfo {name =' user',columns = {name = Column {name =' name',type =' TEXT',   notNull = false,primaryKeyPosition = 0},id =列{name =' id',   type =' INTEGER',notNull = true,primaryKeyPosition = 1},   age =列{name =' age',type =' INTEGER',notNull = false,   primaryKeyPosition = 0}},foreignKeys = []}

我尝试执行简单迁移,我有一个名为User的类,它有两列ID (primary key)NAME TEXT,然后我使用两个用户数据填充数据库,然后我在对象AGE中添加列User,在迁移常量中添加alter table以添加此新列,最后我将数据库1的版本替换为2。 / p>

这是代码

User.class

@Entity(tableName = "user")
  public class User {

  @PrimaryKey
  private int id;

  @ColumnInfo(name = "name")
  private String name;

  @ColumnInfo(name = "age")
  private int age;


  public int getId() {
      return id;
  }

  public void setId(int id) {
      this.id = id;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public int getAge() {
      return age;
  }

  public void setAge(int age) {
      this.age = age;
  }
}

数据库类

@Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
    abstract UserDao userDao();
}

迁移代码

public static Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
    }
 };

并致电

Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
            .addMigrations(MIGRATION_1_2)
            .allowMainThreadQueries()
            .build();

在更改添加AGE的对象并执行迁移之前,我添加了两个寄存器并且它可以正常工作。

执行迁移后,我只是尝试添加一个新用户:

  User user = new User();
  user.setName("JoooJ");
  user.setId(3);
  user.setAge(18);

  List<User> userList = new ArrayList<>();
  userList.add(user);
  App.database(this).userDao().insertAll(userList);  // The crash happens here

其他信息:

Android Studio 3和我没有在实际测试中进行测试。

依赖关系:

compile "android.arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9-1"

compile "android.arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3

有人可以帮助我,我真的不知道我做错了什么或者是不是错误。

9 个答案:

答案 0 :(得分:37)

错误消息很难解析,但有区别:

  

TableInfo {name ='user',columns = {name = Column {name ='name',type ='TEXT',notNull = false,primaryKeyPosition = 0},age = Column {name ='age',type ='INTEGER', notNull = true ,primaryKeyPosition = 0},id =列{name ='id',type ='INTEGER',notNull = true,primaryKeyPosition = 1}},foreignKeys = []}发现:

实测值

  

TableInfo {name ='user',columns = {name = Column {name ='name',type ='TEXT',notNull = false,primaryKeyPosition = 0},id = Column {name ='id',type ='INTEGER',notNull = true,primaryKeyPosition = 1},age =列{name ='age',type ='INTEGER', notNull = false ,primaryKeyPosition = 0}},foreignKeys = []}

年龄可以为空,但是房间预计它不会为空。

将迁移更改为:

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");

由于此异常解释非常难以解析,因此我创建了a small script来为您完成差异。

示例:

mig "java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=[]} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=[]}"

结果:

expected/found diff

答案 1 :(得分:9)

我也写了一个小的JS脚本,您可以找到https://hrankit.github.io/RoomSQLiteDifferenceFinder/

过程非常简单。

  1. 在“期望的”列中输入“期望的错误日志”,即“左一个”。

  2. 在“找到的”列中输入“找到的错误日志”,这是正确的。

  3. 按Go。按钮。错误日志将转换为JSON。

  4. 按“比较”按钮,然后瞧,您将拥有所需的区别。

此插件可从Android Studio Logcat中找出这两个Expected和Found转储之间的差异。

在此处查看比较图像

答案 2 :(得分:6)

在任何链接中,没有一个答案是正确的。经过大量实验,找到了解决之道。需要使用以下方式编写ALTER查询,以使其起作用:

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")

但是,整数DEFAULT值可以是任何值。

如果要添加“字符串类型”列,请按以下方式添加:

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")

这就像一种魅力。

答案 3 :(得分:4)

如果要添加Integer type列,请添加此代码

database.execSQL("ALTER TABLE users"
                    + " ADD COLUMN year INTEGER NOT NULL DEFAULT 0 ")

答案 4 :(得分:3)

我今天遇到了这个问题,我刚刚将int中的Integer字段更改为Entities。由于int不能为空,但是Integer对象可以为空。

答案 5 :(得分:1)

如果您获得的不是notNull差异,则可以简单地使用@NonNull批注标记您的类字段,或者使用ALTER TABLE更改您的sql。但是,如果您遇到不同的列类型差异,例如:TYPE = TEXT,然后找到TYPE =''(COLLATE NOCASE),或者得到了预期的INTEGER,找到INT,则唯一的解决方案是删除并重新创建表。 Sqlite不允许更改列类型。

在Sqlite中使用INTEGER而不是INT,并使用@ColumnInfo(collat​​e = NOCASE)标记Java实体(如果在Sqlite中使用NOCASE)。

在app \ schemas下查看json文件,以获取预期查询的sql。

static final Migration MIGRATION_2_3= new Migration(2, 3) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {

            database.execSQL("DROP TABLE IF EXISTS table_tmp");

            database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

            database.execSQL("insert into table_tmp (`id`, `name` , ...");

            database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

            database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

            database.execSQL("DROP TABLE IF EXISTS table");

            database.execSQL("alter table table_tmp rename to table");

        }
    };

答案 6 :(得分:0)

我在kotlin中没有notNull差异,并且发现了如下异常情况

Expected:
TableInfo{name='enDic', columns={definition=Column{name='definition', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, favourite=Column{name='favourite', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, word=Column{name='word', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, usage=Column{name='usage', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='enDic', columns={usage=Column{name='usage', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, definition=Column{name='definition', type='text', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, favourite=Column{name='favourite', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, word=Column{name='word', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}

然后我使用下面提到的代码来解决该问题

@Entity(tableName = "enDic")
data class Word(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_id")
    var _id: Int?,

    @ColumnInfo(name = "word")
    var word: String?,

    @ColumnInfo(name = "definition")
    var definition: String,

    @ColumnInfo(name = "favourite")
    var favourite: Int?,

    @ColumnInfo(name = "usage")
    var usage: Int?

)

代替此代码

@Entity(tableName = "enDic")
data class Word(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_id")
    var _id: Int,

    @ColumnInfo(name = "word")
    var word: String,

    @ColumnInfo(name = "definition")
    var definition: String,

    @ColumnInfo(name = "favourite")
    var favourite: Int,

    @ColumnInfo(name = "usage")
    var usage: Int

)

答案 7 :(得分:0)

@HRankit 在他的回答中提到的工具:link 我今天尝试时似乎不起作用。如果您也是这种情况,请继续阅读:

如果有人为问题中提到的错误消息而苦苦挣扎,并且有一个包含大量列的巨大表,您可能需要尝试使用 this 在线工具来检查预期和找到的架构。

在生成 <db_version_number>.json(例如:13.json)文件之前决定列名和数据类型也非常重要。如果 json 文件已经生成,并且之后您对 Entity 类进行了一些更改,您可能需要删除 json 文件并重建项目以使用正确的值集生成它。

最后,您应该检查有关迁移本身的 sql 语句。

答案 8 :(得分:0)

我遇到了 roomVersion '2.4.0-alpha01' 的问题,这个版本没有为我的情况生成索引表

@Entity(
    tableName = "SoundRules",
    indices = [
        Index(value = ["remoteId"], unique = true)
    ]
) 

我解决了刚刚将房间版本更新为“2.4.0-alpha03”的问题

相关问题