Как перенести БД комнаты с 1 на 2?

Я пытаюсь перенести мою базу данных 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(). Я попробовал это в действии с использованием базы данных, но ошибка была той же, поэтому я удалил этот вызов.

Как я могу это исправить?

Как вы строили свою БД до изменения схемы? Эта часть не должна была измениться, верно? Я сомневаюсь, что вы хотите сделать это в своей деятельности. Реализация одноэлементного шаблона, вероятно, лучше всего, поскольку вам действительно нужен только один экземпляр базы данных в вашем приложении.

zen_of_kermit 20.03.2022 21:42
1
1
49
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, вы не вызываете функцию сборки и, вероятно, имеете другие средства построения базы данных (вызывая 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
    • Строка соответствует TEXT, а также NOT NULL. Нить? не было бы NOT NULL

Однако, если ИЗМЕНЕНИЕ таблицы и добавление столбца с 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/… Буду признателен за ваш опыт :)

BlazeCodeDev 21.03.2022 18:49

Другие вопросы по теме