Миграция базы данных объектов не обрабатывает миграцию ALTER TABLE

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 = Столбец {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 = 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;
  }
}

Класс базы данных

@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

Может кто-нибудь мне помочь, я действительно не знаю, что я делаю неправильно или если это ошибка.

Ответ 1

Сообщение об ошибке сложно разобрать, но есть разница:

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

Возраст недействителен, но Room ожидает, что он не будет нулевым.

Измените свою миграцию на:

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

Поскольку это объяснение исключений ОЧЕНЬ сложно разобрать, я создал небольшой скрипт, который делает 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=[]}"

Результат:

expected/found diff

Ответ 2

Я тоже написал небольшой JS-скрипт, который вы можете найти https://hrankit.github.io/RoomSQLiteDifferenceFinder/

Процесс довольно прост.

  1. Введите журнал ожидаемых ошибок в столбец Ожидаемый, который является левым.

  2. Введите журнал ошибок Found в столбце Found, который является правильным.

  3. Нажмите Go. кнопка. Журналы ошибок преобразуются в JSON.

  4. Нажмите кнопку сравнения и вуаля, у вас есть разница, что вам нужно.

Этот плагин обнаруживает разницу в двух ожидаемых и найденных дампах из Android Studio Logcat.

Оформить заказ изображение сравнения здесь

Ответ 3

Если вы получаете различия notNull, вы можете просто пометить поле класса аннотацией @NonNull или изменить свой sql с помощью ALTER TABLE. Но если вы получаете различия типов столбцов, такие как ожидаемый: TYPE = TEXT, затем найденный TYPE = '' (COLLATE NOCASE) или ожидаемый INTEGER, найденный INT, тогда единственное решение - удалить и заново создать вашу таблицу. Sqlite не позволяет изменять типы столбцов.

Посмотрите файл json в app\schemas, чтобы получить 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");

        }
    };

Ответ 4

Ни один из ответов не верен ни в одной из ссылок. После долгих экспериментов нашел способ для этого. Запрос ALTER должен быть написан следующим образом, чтобы он работал:

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

Однако значение Integer DEFAULT может быть любым.

Если вы хотите добавить столбец типа String, добавьте его следующим образом:

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

Это работает как шарм.

Ответ 5

Я столкнулся с этой проблемой сегодня, я просто изменил поля int на Integer в Entities. Поскольку int не может быть нулевым, но объекты Integer могут быть нулевыми.