Room Database Migration无法正确处理ALTER TABLE迁移

dio*_*jme 26 sqlite android 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 = Column {name ='age',type ='INTEGER ',notNull = true,primaryKeyPosition = 0},id = Column {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 = Column {name ='age',type ='INTEGER',notNull = false,primaryKeyPosition = 0}},foreignKeys = []}

我正在尝试执行一个简单的迁移,我有一个被调用的类User,它有两列ID (primary key),NAME TEXT然后我用两个用户数据填充数据库,然后我AGE在对象中添加列,User并在迁移常量我添加一个alter table来添加这个新列,最后我将数据库1的版本替换为2.

这是代码

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

数据库类

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

迁移代码

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");
    }
 };
Run Code Online (Sandbox Code Playgroud)

它打电话

Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
            .addMigrations(MIGRATION_1_2)
            .allowMainThreadQueries()
            .build();
Run Code Online (Sandbox Code Playgroud)

在更改对象添加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
Run Code Online (Sandbox Code Playgroud)

其他信息:

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
Run Code Online (Sandbox Code Playgroud)

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

Ben*_*fez 49

错误消息很难解析,但有一点不同:

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 = []}找到:

发现

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 = []}

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

将您的迁移更改为:

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");
Run Code Online (Sandbox Code Playgroud)

由于此异常解释非常难以解析,因此我创建了一个为您执行diff 的小脚本.

例:

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=[]}"
Run Code Online (Sandbox Code Playgroud)

结果:

预期/发现差异

  • 像魅力一样工作,感谢您的帮助。现在我明白了,当您执行迁移时,您必须为要填充的新列指定 DEFAULT 值。 (4认同)
  • 我还发现了一些有趣的事情:如果您在模型中的 @Entity 注释中创建了索引,则还需要在迁移脚本中的 SQL 中执行此操作,例如 `CREATE INDEX `index_History_nodeId` ON `History` (`nodeId`)`如果你不这样做,你会得到一个“迁移没有正确处理” (3认同)

小智 12

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

这个过程非常简单.

  1. 在Expected列中输入预期的错误日志,即Left One.

  2. 在Found列中输入Found错误日志,这是正确的.

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

  4. 按比较按钮和Voila,您将获得所需的差异.

此插件发现Android Studio Logcat中两个Expected和Found转储的差异.

在这里查看比较图像

  • 虽然这在理论上可以回答这个问题,[最好](// meta.stackoverflow.com/q/8259)在这里包含答案的基本部分,并提供参考链接. (2认同)
  • 这个工具现在不起作用,你能重新检查一下吗? (2认同)

Pra*_*ngh 8

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

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")
Run Code Online (Sandbox Code Playgroud)

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

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

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")
Run Code Online (Sandbox Code Playgroud)

这就像一个魅力。


Zai*_*rza 6

我今天遇到了这个问题,我只是将int字段更改为Integerin Entities。Asint不能为空,但Integer对象可以为空。


for*_*yar 5

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

database.execSQL("ALTER TABLE users"
                    + " ADD COLUMN year INTEGER NOT NULL DEFAULT 0 ")
Run Code Online (Sandbox Code Playgroud)