Я пытаюсь перенести мою базу данных Room на следующую версию, и я продолжаю получать ту же ошибку:
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
Единственная разница между версиями моей базы данных заключается в том, что я добавил новый столбец. Миграция выполняется следующим образом:
@Database(
version = 2,
entities = [Note::class],
exportSchema = true)
abstract class AppDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDAO
companion object {
fun build(context: Context) = Room.databaseBuilder(context, AppDatabase::class.java, "NotesDatabase")
.addMigrations(MIGRATION_1_2).build()
}
}
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Notes ADD COLUMN image STRING")
}
}
Я не уверен, правильно ли я это реализовал. Ошибка говорит мне, что я должен как-то вызвать .build(). Я попробовал это в действии с использованием базы данных, но ошибка была той же, поэтому я удалил этот вызов.
Как я могу это исправить?
Похоже, вы не вызываете функцию сборки и, вероятно, имеете другие средства построения базы данных (вызывая databaseBuilder).
например В действии/фрагменте у вас есть что-то вроде: -
class MainActivity : AppCompatActivity() {
lateinit var db: AppDatabase
lateinit var dao: NoteDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = Room.databaseBuilder(this,AppDatabase::class.java,"NotesDatabase")
.allowMainThreadQueries()
.build()
dao = db.noteDao()
dao.getAllNotes()
}
}
Это после изменения объекта Note и увеличения версии до 2 дает результаты, например. :-
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
Вместо этого вам нужно вызвать функцию сборки AppDatabase.
Таким образом, вышеприведенное станет: -
class MainActivity : AppCompatActivity() {
lateinit var db: AppDatabase
lateinit var dao: NoteDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = AppDatabase.build(this) //<<<<<<<<<< CHANGED
dao = db.noteDao()
dao.getAllNotes() // Forces database access/open
}
}
ОДНАКО, вы получите сообщение об ожидаемой/найденной проблеме примерно так: -
2022-03-21 09:15:20.385 14533-14533/a.a.so71549033kotlinroommigration E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so71549033kotlinroommigration, PID: 14533
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so71549033kotlinroommigration/a.a.so71549033kotlinroommigration.MainActivity}: java.lang.IllegalStateException: Migration didn't properly handle: notes(a.a.so71549033kotlinroommigration.Note).
Expected:
TableInfo{name='notes', columns = {noteText=Column{name='noteText', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, image=Column{name='image', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, noteId=Column{name='noteId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='notes', columns = {noteText=Column{name='noteText', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, image=Column{name='image', type='STRING', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}, noteId=Column{name='noteId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Это связано с тем, что НИТЬ с точки зрения помещения не является допустимым типом столбца.
В приведенном выше вы можете увидеть/извлечь ожидаемую комнату: -
image=Column{name='image', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}
НО Комната найдена:-
image=Column{name='image', type='STRING', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}
Room принимает только типы столбцов INTEGER, REAL, TEXT и BLOB (хотя SQLite гораздо более гибок с типами столбцов).
Тип зависит от типа переменной, кроме того, Room также очень специфичен в отношении других частей определения, например, должен ли быть NOT NULL частью и должен ли DEFAULT быть частью определения или нет.
Однако Room позволяет точно установить, каким должно быть определение столбца. Если вы вносите изменения в объект (ы) и компилируете проект, тогда Java-код генерируется Room, включая SQL, используемый для создания таблицы (таблиц), и в этом SQL находятся ожидаемые определения столбцов.
В представлении проекта Android Studio вы увидите Java (generated)
, в классах/файлах под ним будет несколько классов, один из которых будет AppDatabase_Impl. Внутри этого класса будет метод создать все таблицы. В рамках этого метода находится SQL для всех таблиц.
например :-
var image: String
Однако, если ИЗМЕНЕНИЕ таблицы и добавление столбца с NOT NULL, тогда SQLite требует, чтобы было предоставлено значение DEFAULT, которое не равно нулю, согласно: - Если задано ограничение NOT NULL, то столбец должен иметь значение по умолчанию, отличное от NULL.https://www.sqlite.org/lang_altertable.html
Таким образом, в приведенном выше случае для того, чтобы найти то, что ожидает комната, ALTER SQL должен быть: -
ALTER TABLE Notes ADD COLUMN image TEXT NOT NULL DEFAULT 'unknown'
'unknown'
может быть что угодноБольшое спасибо! Это решило проблему. Однако теперь у меня другая проблема с базой данных, в которой говорится, что таблица не существует. Я создал новый вопрос для этого. stackoverflow.com/questions/71561929/… Буду признателен за ваш опыт :)
Как вы строили свою БД до изменения схемы? Эта часть не должна была измениться, верно? Я сомневаюсь, что вы хотите сделать это в своей деятельности. Реализация одноэлементного шаблона, вероятно, лучше всего, поскольку вам действительно нужен только один экземпляр базы данных в вашем приложении.