Миграция базы данных объектов не обрабатывает миграцию 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/
Процесс довольно прост.
-
Введите журнал ожидаемых ошибок в столбец Ожидаемый, который является левым.
-
Введите журнал ошибок Found в столбце Found, который является правильным.
-
Нажмите Go. кнопка. Журналы ошибок преобразуются в JSON.
-
Нажмите кнопку сравнения и вуаля, у вас есть разница, что вам нужно.
Этот плагин обнаруживает разницу в двух ожидаемых и найденных дампах из 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
могут быть нулевыми.