[Jetpack] Utilice la migración en la sala para actualizar el manejo de excepciones de la base de datos (migración de varias versiones de la base de datos | la función fallbackToDestructiveMigration() maneja las excepciones de actualización)





1. La herramienta de migración Room#Migration actualiza la base de datos



La herramienta de migración de bases de datos Room Migration es parte de Android Jetpack Architecture Components (componentes de arquitectura) , que es una herramienta de migración de bases de datos para proporcionar soluciones de migración automatizadas para bases de datos creadas con el marco Room en Android ;


La herramienta de migración de la base de datos Room Migration se utiliza de la siguiente manera:

  • Modificación de la base de datos: modificar la estructura de la tabla de la base de datos;
  • Código de migración: escriba el código de migración para cada versión de la base de datos;
  • Actualización automática: detecta automáticamente el número de versión de la base de datos y realiza automáticamente la migración de datos al ejecutar la aplicación;

Guarde los datos de la base de datos antes de la migración: al cambiar el esquema en la base de datos de Room en la aplicación, se deberá realizar una migración de la base de datos para conservar los datos antiguos y evitar que la aplicación se bloquee;

Operación automática: la herramienta de migración de la base de datos Room Migration creará automáticamente un archivo de migración y lo aplicará a la base de datos para mantener la base de datos SQLite actualizada;





2. Migración de múltiples versiones de bases de datos



En la base de datos de la versión 1 original, hay tres campos de la siguiente manera: id, nombre, edad;

@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
}

Actualice de la versión 1 de la base de datos a la versión 2 de la base de datos, agregue el campo de sexo;

        /**
         * 数据库版本 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")
            }
        }

Actualice de la versión 2 de la base de datos a la versión 3 de la base de datos, agregando el campo de grado;

        /**
         * 数据库版本 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")
            }
        }

Antes de que el usuario ejecute la base de datos, es posible instalar cualquier versión de la base de datos versión 1/versión 2/versión 3;


Base de datos versión 1 -> base de datos versión 3 proceso de actualización:

Si el usuario estaba ejecutando la versión 1 de la base de datos antes, cuando ejecute la última aplicación, primero ejecute

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

Para la operación de migración correspondiente al objeto de migración, primero actualice de la versión 1 de la base de datos a la versión 2 de la base de datos;

y luego ejecutar

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

La operación de migración correspondiente al objeto de migración, actualización de la versión 2 de la base de datos a la versión 3 de la base de datos;


Base de datos versión 2 -> Base de datos versión 3 proceso de actualización:

Si la versión anterior de la base de datos en el teléfono móvil del usuario es la versión 2, cuando ejecute la última aplicación, ejecute directamente

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

La operación de migración correspondiente al objeto de migración, actualización de la versión 2 de la base de datos a la versión 3 de la base de datos;





3. Manejo de excepciones de la base de datos: función RoomDatabase.Builder#fallbackToDestructiveMigration()



En el blog anterior 【Jetpack】Uso de la migración en la sala para actualizar la base de datos (modificar la clase de entidad de la entidad - cambiar el modelo de datos | crear la clase de migración de migración | modificar la versión de la base de datos | ejemplo de código), se explicó cómo usar la migración para actualizar la base de datos ;

Primero, cree la clase de migración Migración,

    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")
            }
        }
	}

Luego, modifique la versión de la base de datos;

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

Si la versión de la base de datos solo modifica en la anotación @Database de RoomDatabase , pero no se crea la clase de migración de migración correspondiente, se producirá una IllegalStateException;

El mensaje de error es el siguiente:

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

inserte la descripción de la imagen aquí

Para lidiar con las excepciones anteriores, debe ejecutar la función RoomDatabase.Builder#fallbackToDestructiveMigration() al crear RoomDatabase.Builder . Luego, al usar Migración para migrar la base de datos, si ocurre una excepción, la tabla de la base de datos se reconstruirá, pero el los datos de la base de datos anterior se vaciarán en consecuencia. ;

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

Cuando se ejecuta de nuevo, los resultados del registro impreso son los siguientes:

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)]

El registro impreso en la primera línea es Observer#onChanged 回调, List<Student>: []que la base de datos actual está vacía y los datos anteriores se han borrado. En este momento, el registro impreso son los datos recién insertados cuando la aplicación se está ejecutando;





4. Ejemplo de código completo



Dirección del código: https://github.com/han1202012/Room_ViewModel_LiveData

おすすめ

転載: blog.csdn.net/han1202012/article/details/131040728