discuz完整迁移_完整的会议室迁移指南

discuz完整迁移

In this article let’s examine a few situations you might face while you are developing your app using ROOM database. Sometimes there is a need to change our database schema by adding fields or entities.

在本文中,我们将研究在使用ROOM数据库开发应用程序时可能遇到的几种情况。 有时需要通过添加字段或实体来更改我们的数据库架构。

First of all, let’s assume that we have created an AppDatabase with a data access object class UserDao and a User entity.

首先,假设我们已经创建了一个AppDatabase其中包含一个数据访问对象类UserDao和一个User实体。

@Database(entities = arrayOf(User::class), version = 0)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}@Entity
data class User(@PrimaryKey val id: Int)

Database version unchanged

数据库版本不变

Let’s say that we want to add a field name to the User entity.

假设我们要向User实体添加一个字段name

@Entity
data class User(@PrimaryKey val id: Int, val name: String)

If you leave unchanged the schema version but you modify the User entity, when you will try to access your database, Room will throw an exception.

如果您保留架构版本不变但修改了User实体,则当您尝试访问数据库时,Room将引发异常。

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

Database version increased but no migration code provided

数据库版本增加,但未提供迁移代码

Now let’s increase the database version to 1 as suggested by the exception message.

现在,根据异常消息的建议,将数据库版本增加到1。

@Database(entities = arrayOf(User::class), version = 1)

In that case Room doesn’t know how to migrate database from version 0 to version 1 and throws below exception when you try to open it:

在这种情况下,Room不知道如何将数据库从版本0迁移到版本1,并且在尝试打开数据库时抛出以下异常:

java.lang.IllegalStateException: A migration from 0 to 1 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.

In the exception message Room suggests us two solutions:

在例外消息中,Room为我们提供了两种解决方案:

  • provide some migration code from version 0 to 1

    提供一些从版本0到版本1的迁移代码
  • allow database to be deleted and re-created

    允许删除并重新创建数据库

Database version increased, fallback to destructive migration

数据库版本增加,回退到破坏性迁移

The second option is the easiest one, since we don’t need to provide any migration code, we only need to add fallbackToDestructiveMigration in Room builder.

第二个选项是最简单的一种,因为我们不需要提供任何迁移代码,因此只需要在“房间”构建器中添加fallbackToDestructiveMigration

val appContext = context.applicationContextval db = Room.databaseBuilder(appContext, AppDatabase::class.java)             .fallbackToDestructiveMigration()
.build()

In that case the doc states it allows Room to destructively recreate database tables. In other words database will be deleted and re-created and all the data stored in it will be lost.

在那种情况下,文档指出允许Room破坏性地重新创建数据库表。 换句话说,数据库将被删除并重新创建,并且其中存储的所有数据都将丢失。

Database version increased, migration code provided

数据库版本增加,提供了迁移代码

If you don’t want to erase the database and loose your data, which is generally the goal you try to achieve, you need to provide a migration code.

如果您不想删除数据库并丢失数据(通常这是您试图实现的目标),则需要提供迁移代码。

Room library supports incremental migrations with Migration classes. Each Migration class defines a migration path between a startVersion and endVersion by overriding migrate()method. At run-time, when an app update requires a database version upgrade, Room runs the migrate() method from one or more Migration class to migrate the database to the latest version.

Room库通过Migration类支持增量迁移。 每个MigrationendVersion通过覆盖endVersion migrate()方法来定义startVersionendVersion之间的迁移路径。 在运行时,当应用程序更新需要数据库版本升级时,Room从一个或多个Migration类运行migrate()方法,以将数据库迁移到最新版本。

Even if you are not familiar with SQL commands it’s quite straightforward to understand below migration code.

即使您不熟悉SQL命令,也很容易理解下面的迁移代码。

val MIGRATION_0_1 = object : Migration(0, 1) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE User ADD COLUMN name TEXT NOT NULL DEFAULT '' ")
}
}

The last step is to add this Migration class to Room builder.

最后一步是将此“ Migration类添加到“房间”构建器中。

val applicationContext = context.applicationContextval db = Room.databaseBuilder(appContext, AppDatabase::class.java)             .addMigrations(MIGRATION_0_1)
.build()

Migration code provided: add an Entity

提供的迁移代码:添加实体

Now we want to save more information on our users, we want to save their address, for that purpose we create an Address entity.

现在,我们想保存有关用户的更多信息,我们想保存他们的地址,为此,我们创建一个Address实体。

@Entity
data class Address(@PrimaryKey val id: Int, addressLine: String)

We increase the database version to 2.

我们将数据库版本增加到2。

@Database(
entities = arrayOf(User::class, Address::class), version = 2)

We provide some migration code.

我们提供了一些迁移代码。

val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE Address
(id INTEGER NOT NULL, addressLine TEXT NOT NULL, PRIMARY KEY(id))
")
}
}

Finally we add that migration code to Room builder

最后,我们将该迁移代码添加到“房间”构建器中

val applicationContext = context.applicationContextval db = Room.databaseBuilder(appContext, AppDatabase::class.java)             .addMigrations(MIGRATION_0_1, MIGRATION_1_2)
.build()

Special tip

特别提示

Now if you want to add a new entity with a lot of fields and with some nested objects. It would be quite difficult to write by yourself the migration code. Fortunately there is a way to make things much easier. By setting the annotation processor exportSchema to true, Room will export at compile time the database schema as JSON format.

现在,如果要添加具有许多字段和一些嵌套对象的新实体。 自己编写迁移代码将非常困难。 幸运的是,有一种方法可以使事情变得容易得多。 通过将注释处理器exportSchema设置为true,Room将在编译时将数据库模式导出为JSON格式。

@Database(
entities = arrayOf(User::class, Address::class), version = 2, exportSchema = true)

In your gradle file you can specify the location where the schema will be exported.

gradle文件中,您可以指定导出架构的位置。

android {defaultConfig {kapt {arguments {
arg("room.schemaLocation", "$projectDir/roomSchemas".toString())

}}
}

Now let’s have a look at the generated JSON file but only the part corresponding to the Address entity.

现在,让我们看一下生成的JSON文件,但仅查看与Address实体相对应的部分。

{
"tableName": "Address",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `addressLine` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "addressLine",
"columnName": "addressLine",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}

You can see in the line starting by createSql the SQL command responsible for creating the Address table in your database. You can copy this line, replace ${TABLE_NAME} by the entity name Address and put it to the migration code.

您可以在createSql开头的行中看到负责在数据库中创建Address表SQL命令。 您可以复制此行,替换${TABLE_NAME} 按实体名称Address并将其放入迁移代码。

val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `Address`
(`id` INTEGER NOT NULL, `addressLine` TEXT NOT NULL,
PRIMARY KEY(`id`))
")
}
}

Migration test

迁移测试

As a good software developer we follow the TDD (Test Driven Development) principle, so we want to test our migrations.

作为一个好的软件开发人员,我们遵循TDD(测试驱动开发)原则,因此我们想测试我们的迁移。

The exported JSON files, that we saw earlier, represent your database’s schema history. You should store these files in your version control system, as it allows Room library to create older versions of the database for testing purposes.

我们之前看到的导出的JSON文件代表了数据库的架构历史。 您应该将这些文件存储在版本控制系统中,因为它允许Room库创建较旧版本的数据库以进行测试。

Before you can test your migrations, you must add the location of the exported schema as an asset folder.

在测试迁移之前,必须将导出的架构的位置添加为资产文件夹。

android {sourceSets {androidTest.assets.srcDirs +=
files("$projectDir/roomSchemas".toString())
}
}

and add the room testing library to the list of dependencies.

并将房间测试库添加到依赖项列表中。

dependencies {
androidTestImplementation "androidx.room:room-testing:2.2.5"
}

Let’s test the migration from version 0 to 1.

让我们测试一下从版本0到版本1的迁移。

The testing package provides a MigrationTestHelper class, which can read exported schema files and then create the database in an older schema. We create database with version 0.

该测试包提供了一个MigrationTestHelper类,该类可以读取导出的架构文件,然后在较旧的架构中创建数据库。 我们创建版本为0的数据库。

@RunWith(AndroidJUnit4::class)
class MigrationTest { companion object {
private const val TEST_DB = "migration-test"
} @get:Rule
val helper: MigrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
NdDatabase::class.java.canonicalName,
FrameworkSQLiteOpenHelperFactory()) @Test
fun migrationFrom0To1() {
//Create database with version 0
val db = helper.createDatabase(TEST_DB, 0)

Then we need to insert the User entity into the database, but we cannot use the DAO class because it expects the latest schema.

然后,我们需要将User实体插入数据库中,但是我们不能使用DAO类,因为它需要最新的架构。

val values = ContentValues().apply {
put("id", 5)
}
db.insert("User", SQLiteDatabase.CONFLICT_REPLACE, values)

Then we need to run and validate the migration code.

然后,我们需要运行并验证迁移代码。

helper.runMigrationsAndValidate(TEST_DB, 1, true, MIGRATION_0_1)

However to have access, in instrumentation test, to MIGRATION_0_1 class we need to add @VisibleForTesting annotation.

但是,要在工具测试中访问MIGRATION_0_1类,我们需要添加@VisibleForTesting批注。

@VisibleForTesting
val MIGRATION_0_1 = object : Migration(0, 1) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE User ADD COLUMN name TEXT NOT NULL DEFAULT '' ")
}
}

Now we need to verify that the data we inserted earlier has been well saved and well preserved during the migration operation. For that we open the database with the latest version.

现在,我们需要验证我们先前插入的数据在迁移操作期间是否已妥善保存和保留。 为此,我们使用最新版本打开数据库。

database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
NdDatabase::class.java, TEST_DB)
.addMigrations(MIGRATION_0_1, MIGRATION_1_2)
.build()

and then we retrieve the user from the database and check if it has been well saved.

然后我们从数据库中检索用户,并检查是否保存正确。

val dbUser = database.userDao().getUser()
assertEquals(5, dbUser.id)
assertEquals("", dbUser.id)

The complete code…

完整的代码…

@RunWith(AndroidJUnit4::class)
class MigrationTest {
    companion object {
        private const val TEST_DB = "migration-test"
    }


    @get:Rule
    val helper: MigrationTestHelper = MigrationTestHelper(
            InstrumentationRegistry.getInstrumentation(),
            NdDatabase::class.java.canonicalName,
            FrameworkSQLiteOpenHelperFactory())


    @Test
    fun migrationFrom0To1() {


        val values = ContentValues().apply {
            put("id", 5)
        }


        //Create database with version 0
        helper.createDatabase(TEST_DB, 0).apply {
            insert("User", SQLiteDatabase.CONFLICT_REPLACE, values)
            close()
        }
      
        helper.runMigrationsAndValidate(TEST_DB, 1, true, MIGRATION_0_1)


        val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
                NdDatabase::class.java, TEST_DB)
                .addMigrations(MIGRATION_0_1, MIGRATION_1_2)
                .build()


        val dbUser = database.userDao().getUser()
        assertEquals(5, dbUser.id)
        assertEquals("", dbUser.name)
    }
}

翻译自: https://medium.com/swlh/the-complete-room-migration-guide-d88b46c51206

discuz完整迁移

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值