如果只添加新表,房间数据库迁移 [英] Room database migration if only new table is added

查看:32
本文介绍了如果只添加新表,房间数据库迁移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们假设,我有一个简单的 Room 数据库:

Let't assume, I have a simple Room database:

@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

现在,我要添加一个新实体:Pet 并将版本升级为 2:

Now, I'm adding a new entity: Pet and bumping version to 2:

@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

当然,Room 会抛出异常:java.lang.IllegalStateException:需要从 1 迁移到 2.

Of course, Room throws an exception: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

假设,我没有更改 User 类(所以所有数据都是安全的),我必须提供只创建一个新表的迁移.因此,我正在查看 Room 生成的类,搜索生成的查询以创建我的新表,将其复制并粘贴到迁移中:

Assuming, I haven't changed User class (so all data is safe), I have to provide migration which just creates a new table. So, I'm looking into classes generated by Room, searching for generated query to create my new table, copying it and pasting into migration:

final Migration MIGRATION_1_2 =
        new Migration(1, 2) {
            @Override
            public void migrate(@NonNull final SupportSQLiteDatabase database) {
                database.execSQL("CREATE TABLE IF NOT EXISTS `Pet` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))");
            }
        };

但是我觉得手动操作很不方便.有没有办法告诉 Room:我没有接触任何现有的表,所以数据是安全的.请为我创建迁移?

However I find it inconvenient to do it manually. Is there a way to tell Room: I'm not touching any of the existing table, so data is safe. Please create migration for me?

推荐答案

Room 确实没有有一个好的迁移系统,至少在 2.1.0 之前没有-alpha03.

Room does NOT have a good Migration System, at least not until 2.1.0-alpha03.

因此,在我们拥有更好的迁移系统之前,有一些解决方法可以在房间中轻松迁移.

So, until we have better Migration System, there are some workarounds to have easy Migrations in the Room.

因为没有像 @Database(createNewTables = true)MigrationSystem.createTable(User::class) 这样的方法,应该有一个或另一个,唯一可能的方法是运行

As there is no such method as @Database(createNewTables = true) or MigrationSystem.createTable(User::class), which there should be one or other, the only possible way is running

CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))

在您的 migrate 方法中.

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

为了获得上述 SQL 脚本,您有 4 种方法

In order to get above SQL script, you have 4 ways

基本上,您必须编写与 Room 生成的脚本匹配的上述脚本.这种方式可行,不可行.(假设您有 50 个字段)

Basically, you have to write the above script that will match the script that Room generates. This way is possible, not feasible. (Consider you have 50 fields)

如果在 @Database 注释中包含 exportSchema = true,Room 将在项目文件夹的/schemas 中生成数据库模式.用法是

If you include exportSchema = true inside your @Database annotation, Room will generate database schema within /schemas of your project folder. The usage is

@Database(entities = [User::class], version = 2, exportSchema = true)
abstract class AppDatabase : RoomDatabase {
   //...
}

确保您在应用模块的 build.grade 中包含以下几行

Make sure that you have included below lines in build.grade of your app module

kapt {
    arguments {
        arg("room.schemaLocation", "$projectDir/schemas".toString())
    }
} 

当您运行或构建项目时,您将获得一个 JSON 文件 2.json,其中包含 Room 数据库中的所有查询.

When you run or build the project you will get a JSON file 2.json, which has all the queries within your Room database.

  "formatVersion": 1,
  "database": {
    "version": 2,
    "identityHash": "325bd539353db508c5248423a1c88c03",
    "entities": [
      {
        "tableName": "User",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": true
          },

因此,您可以在 migrate 方法中包含上述 createSql.

So, you can include the above createSql within you migrate method.

如果您不想导出架构,您仍然可以通过运行或构建将生成 AppDatabase_Impl.java 文件的项目来获取查询.并在您可以拥有的指定文件中.

If you don't want to export schema you can still get the query by running or building the project which will generate AppDatabase_Impl.java file. and within the specified file you can have.

@Override
public void createAllTables(SupportSQLiteDatabase _db) {
  _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");

createAllTables 方法中,会有所有实体的创建脚本.您可以获取它并包含在您的 migrate 方法中.

Within createAllTables method, there will be the create scripts of all the entities. You can get it and include in within you migrate method.

正如您可能猜到的,Room 会在编译时间内生成所有上述 schemaAppDatabase_Impl 文件,并使用您添加的注释处理

As you might guess, Room generates all of the above mentioned schema, and AppDatabase_Impl files within compilation time and with Annotation Processing which you add with

kapt "androidx.room:room-compiler:$room_version"

这意味着您也可以做同样的事情,制作自己的注释处理库,为您生成所有必要的创建查询.

That means you can also do the same and make your own annotation processing library that generates all the necessary create queries for you.

想法是为@Entity@Database的Room注解做一个注解处理库.以一个用 @Entity 注释的类为例.这些是您必须遵循的步骤

The idea is to make an annotation processing library for Room annotations of @Entity and @Database. Take a class that is annotated with @Entity for example. These are the steps you will have to follow

  1. 创建一个新的 StringBuilder 并附加CREATE TABLE IF NOT EXISTS"
  2. class.simplename@EntitytableName 字段获取表名.将它添加到您的 StringBuilder
  3. 然后为您班级的每个字段创建 SQL 列.通过字段本身或通过 @ColumnInfo 注释获取字段的名称、类型、可为空性.对于每个字段,您必须将列的 id INTEGER NOT NULL 样式添加到您的 StringBuilder.
  4. 通过@PrimaryKey
  5. 添加主键
  6. 添加ForeignKeyIndices(如果存在).
  7. 完成后将其转换为字符串并将其保存在您要使用的某个新类中.例如,保存如下
  1. Make a new StringBuilder and append "CREATE TABLE IF NOT EXISTS "
  2. Get the table name either from class.simplename or by tableName field of @Entity. Add it to your StringBuilder
  3. Then for each field of your class create columns of SQL. Take the name, type, nullability of the field either by the field itself or by @ColumnInfo annotation. For every field, you have to add id INTEGER NOT NULL style of a column to your StringBuilder.
  4. Add primary keys by @PrimaryKey
  5. Add ForeignKey and Indices if exists.
  6. After finishing convert it to string and save it in some new class that you want to use. For example, save it like below

public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

然后,您可以将其用作

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL(UserSqlUtils().createTable)
    }
}

我为自己制作了这样一个库,您可以查看它,甚至在您的项目中使用它.请注意,我制作的库并不完整,它只是满足了我创建表的要求.

I made such a library for myself which you can check out, and even use it in your project. Note that the library that I made is not full and it just fulfills my requirements for table creation.

RoomExtension 更好的迁移

使用 RoomExtension 的应用程序

希望有用.

在撰写此答案时,房间版本为 2.1.0-alpha03,当我给开发人员发送电子邮件时,我收到了

By the time of writing this answer, room version was 2.1.0-alpha03 and when I emailed developers I got a response of

2.2.0

不幸的是,我们仍然缺乏更好的迁移系统.

Unfortunately, we still lack better Migration System.

这篇关于如果只添加新表,房间数据库迁移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆