[Jetpack] Verwenden Sie Migration in Room, um die Behandlung von Datenbankausnahmen zu aktualisieren (Migration mehrerer Datenbankversionen | fallbackToDestructiveMigration()-Funktion behandelt Upgrade-Ausnahmen)





1. Das Room#Migration-Migrationstool aktualisiert die Datenbank



Das Room Migration-Datenbankmigrationstool ist Teil von Android Jetpack Architecture Components (Architekturkomponenten) , einem praktischen Datenbankmigrationstool zur Bereitstellung automatisierter Migrationslösungen für Datenbanken , die mit dem Room-Framework in Android erstellt wurden .


Das Datenbankmigrationstool „Room Migration“ wird wie folgt verwendet:

  • Datenbankänderung: Ändern Sie die Datenbanktabellenstruktur.
  • Migrationscode: Migrationscode für jede Datenbankversion schreiben;
  • Automatische Aktualisierung: Erkennen Sie automatisch die Versionsnummer der Datenbank und führen Sie beim Ausführen der Anwendung automatisch eine Datenmigration durch.

Datenbankdaten vor der Migration speichern: Wenn Sie das Schema in der Room-Datenbank in der Anwendung ändern, muss eine Datenbankmigration durchgeführt werden, um die alten Daten beizubehalten und einen Absturz der Anwendung zu verhindern;

Automatischer Betrieb: Das Room Migration-Datenbankmigrationstool erstellt automatisch eine Migrationsdatei und wendet sie auf die Datenbank an, um die SQLite-Datenbank auf dem neuesten Stand zu halten.





2. Migration mehrerer Datenbankversionen



In der ursprünglichen Datenbank der Version 1 gibt es drei Felder wie folgt: id , name , age ;

@Entity(tableName = "student")
class Student {
    
    
    /**
     * @PrimaryKey 设置主键 autoGenerate 为自增
     * @ColumnInfo name 设置列名称 / typeAffinity 设置列类型
     */
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    var id: Int = 0

    /**
     * 姓名字段
     * 数据库表中的列名为 name
     * 数据库表中的类型为 TEXT 文本类型
     */
    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
    lateinit var name: String

    /**
     * 年龄字段
     * 数据库表中的列名为 age
     * 数据库表中的类型为 INTEGER 文本类型
     */
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    var age: Int = 0
}

Upgrade von Datenbankversion 1 auf Datenbankversion 2, Geschlechtsfeld hinzufügen;

        /**
         * 数据库版本 1 升级到 版本 2 的迁移类实例对象
         */
        val MIGRATION_1_2: Migration = object : Migration(1, 2) {
    
    
            override fun migrate(database: SupportSQLiteDatabase) {
    
    
                Log.i("Room_StudentDatabase", "数据库版本 1 升级到 版本 2")
                database.execSQL("alter table student add column sex integer not null default 1")
            }
        }

Upgrade von Datenbankversion 2 auf Datenbankversion 3, Hinzufügen des Abschlussfelds;

        /**
         * 数据库版本 2 升级到 版本 3 的迁移类实例对象
         */
        val MIGRATION_2_3: Migration = object : Migration(2, 3) {
    
    
            override fun migrate(database: SupportSQLiteDatabase) {
    
    
                Log.i("Room_StudentDatabase", "数据库版本 2 升级到 版本 3")
                database.execSQL("alter table student add column degree integer not null default 1")
            }
        }

Bevor der Benutzer die Datenbank ausführt, ist es möglich, eine beliebige Version der Datenbank Version 1 / Version 2 / Version 3 zu installieren;


Datenbankversion 1 -> Datenbankversion 3-Upgrade-Prozess:

Wenn der Benutzer zuvor Datenbankversion 1 ausgeführt hat, führen Sie ihn beim Ausführen der neuesten Anwendung zuerst aus

val MIGRATION_1_2: Migration = object : Migration(1, 2)

Führen Sie für den Migrationsvorgang, der dem Migrationsobjekt entspricht, zunächst ein Upgrade von Datenbankversion 1 auf Datenbankversion 2 durch.

und dann ausführen

val MIGRATION_2_3: Migration = object : Migration(2, 3)

Der Migrationsvorgang entspricht dem Migrationsobjekt, Upgrade von Datenbankversion 2 auf Datenbankversion 3;


Datenbankversion 2 -> Aktualisierungsprozess für Datenbankversion 3:

Wenn die vorherige Version der Datenbank im Mobiltelefon des Benutzers Version 2 ist, wird sie beim Ausführen der neuesten Anwendung direkt ausgeführt

val MIGRATION_2_3: Migration = object : Migration(2, 3)

Der Migrationsvorgang entspricht dem Migrationsobjekt, Upgrade von Datenbankversion 2 auf Datenbankversion 3;





3. Behandlung von Datenbankausnahmen – Funktion RoomDatabase.Builder#fallbackToDestructiveMigration()



Im vorherigen Blog „Jetpack“ Migration in Room zum Aktualisieren der Datenbank verwenden (Entity-Entitätsklasse ändern – Datenmodell ändern | Migrationsmigrationsklasse erstellen | Datenbankversion ändern | Codebeispiel) wurde erläutert, wie Migration zum Aktualisieren der Datenbank verwendet wird .

Erstellen Sie zunächst die Migrationsklasse Migration.

    companion object {
    
    
        /**
         * 数据库版本 1 升级到 版本 2 的迁移类实例对象
         */
        val MIGRATION_1_2: Migration = object : Migration(1, 2) {
    
    
            override fun migrate(database: SupportSQLiteDatabase) {
    
    
                Log.i("StudentDatabase", "数据库版本 1 升级到 版本 2")
                database.execSQL("alter table student add column sex integer not null default 1")
            }
        }
	}

Ändern Sie dann die Datenbankversion.

@Database(entities = [Student::class], version = 1, exportSchema = false)
abstract class StudentDatabase: RoomDatabase() {
    
    

Wenn die Datenbankversion nur , die entsprechende Migrationsmigrationsklasse jedoch nicht erstellt wird, tritt eine IllegalStateException auf.

Die Fehlermeldung lautet wie folgt:

2023-06-05 10:47:13.635 E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
    Process: kim.hsl.rvl, PID: 31463
    java.lang.RuntimeException: Exception while computing database live data.
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:930)
     Caused by: java.lang.IllegalStateException: A migration from 2 to 4 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.
        at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:117)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:124)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:435)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:331)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
        at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
        at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
        at androidx.room.RoomDatabase.query(RoomDatabase.java:324)
        at androidx.room.util.DBUtil.query(DBUtil.java:83)
        at kim.hsl.rvl.StudentDao_Impl$4.call(StudentDao_Impl.java:125)
        at kim.hsl.rvl.StudentDao_Impl$4.call(StudentDao_Impl.java:122)
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:930) 
    
    
    --------- beginning of system

Fügen Sie hier eine Bildbeschreibung ein

Um mit den oben genannten Ausnahmen umzugehen, müssen beim Erstellen von RoomDatabase.Builder die Funktion RoomDatabase.Builder#fallbackToDestructiveMigration() ausführen . Wenn anschließend Migration zum Migrieren der Datenbank verwendet wird und eine Ausnahme auftritt, wird die Datenbanktabelle neu erstellt, aber die vorherige Datenbankdaten werden entsprechend geleert. ;

                    // 创建数据库
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        StudentDatabase::class.java,
                        "student_database.db")
                        .addMigrations(MIGRATION_1_2)
                        .addMigrations(MIGRATION_2_3)
                        .fallbackToDestructiveMigration()
                        .allowMainThreadQueries() // Room 原则上不允许在主线程操作数据库
                                                  // 如果要在主线程操作数据库需要调用该函数
                        .build()

Beim erneuten Ausführen lauten die gedruckten Protokollergebnisse wie folgt:

2023-06-05 10:52:41.757 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: []
2023-06-05 10:52:42.154 I/Room_MainActivity: 插入数据 S1 : Student(id=0, name='Tom', age=18)
2023-06-05 10:52:42.158 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=1, name='Tom', age=18)]
2023-06-05 10:52:42.664 I/Room_MainActivity: 插入数据 S2 : Student(id=0, name='Jerry', age=16)
2023-06-05 10:52:42.666 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=1, name='Tom', age=18), Student(id=2, name='Jerry', age=16)]
2023-06-05 10:52:43.174 I/Room_MainActivity: 更新数据 S2 : Student(id=2, name='Jack', age=60)
2023-06-05 10:52:43.176 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=1, name='Tom', age=18), Student(id=2, name='Jack', age=60)]
2023-06-05 10:52:43.681 I/Room_MainActivity: 删除数据 id = 1
2023-06-05 10:52:43.683 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=2, name='Jack', age=60)]
2023-06-05 10:52:44.182 I/Room_MainActivity: 主动查询 : LiveData : androidx.room.RoomTrackingLiveData@b957950 , 实际数据 : null
2023-06-05 10:52:44.183 I/Room_MainActivity: 主动查询2 : [Student(id=2, name='Jack', age=60)]

Das in der ersten Zeile gedruckte Protokoll zeigt an Observer#onChanged 回调, List<Student>: [], dass die aktuelle Datenbank leer ist und die vorherigen Daten gelöscht wurden. Zu diesem Zeitpunkt handelt es sich bei dem gedruckten Protokoll um die neu eingefügten Daten, wenn die Anwendung ausgeführt wird.





4. Vollständiges Codebeispiel



Codeadresse: https://github.com/han1202012/Room_ViewModel_LiveData

Supongo que te gusta

Origin blog.csdn.net/han1202012/article/details/131040728
Recomendado
Clasificación