java 如果只添加新表,房间数据库迁移

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48399852/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 10:02:59  来源:igfitidea点击:

Room database migration if only new table is added

javaandroiddatabase-migrationandroid-room

提问by Piotr Aleksander Chmielowski

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

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

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

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

现在,我要添加一个新实体:Pet并将版本提高到 2:

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

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

当然,Room 会抛出异常: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

Assuming, I haven't changed Userclass (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:

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

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

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:我没有接触任何现有的表,所以数据是安全的。请为我创建迁移?

采纳答案by musooff

Roomdoes NOThave a good Migration System, at least not until 2.1.0-alpha03.

确实具备良好的迁移系统,至少直到2.1.0-alpha03

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

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

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

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

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

inside your migratemethod.

在你的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`))")
    }
}

In order to get above SQLscript, you have 4 ways

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

1. Write by yourself

1. 自己写

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)

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

2. Export Schema

2. 导出架构

If you include exportSchema = trueinside your @Databaseannotation, Room will generate database schema within /schemas of your project folder. The usage is

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

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

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

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

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

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

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

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

So, you can include the above createSqlwithin you migratemethod.

因此,您可以createSqlmigrate方法中包含上述内容。

3. Get query from AppDatabase_Impl

3. 从 AppDatabase_Impl 获取查询

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.javafile. and within the specified file you can have.

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

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

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

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

4. Annotation Processing.

4. 注释处理。

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

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

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.

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

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

这个想法是为@Entity和 的房间注释制作一个注释处理库@Database。以一个带有注释的类@Entity为例。这些是您必须遵循的步骤

  1. Make a new StringBuilderand append "CREATE TABLE IF NOT EXISTS "
  2. Get the table name either from class.simplenameor by tableNamefield 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 @ColumnInfoannotation. For every field, you have to add id INTEGER NOT NULLstyle of a column to your StringBuilder.
  4. Add primary keys by @PrimaryKey
  5. Add ForeignKeyand Indicesif 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
  1. StringBuilder创建一个新的并附加“如果不存在则创建表”
  2. 获取表名 fromclass.simplename或 by 的tableName字段@Entity。将其添加到您的StringBuilder
  3. 然后为您班级的每个字段创建 SQL 列。通过字段本身或通过@ColumnInfo注释获取字段的名称、类型、可为空性。对于每个字段,您必须将id INTEGER NOT NULL列样式添加到您的StringBuilder.
  4. 添加主键 @PrimaryKey
  5. 添加ForeignKeyIndices如果存在。
  6. 完成后将其转换为字符串并将其保存在您要使用的某个新类中。例如,保存如下
public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

Then, you can use it as

然后,您可以将其用作

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 for better Migration

RoomExtension 以实现更好的迁移

Application that uses RoomExtension

使用 RoomExtension 的应用程序

Hope it was useful.

希望它有用。

UPDATE

更新

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

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

It is expected to have better Migration System in 2.2.0

预计会有更好的移民系统 2.2.0

Unfortunately, we still lack better Migration System.

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

回答by Viswanath Kumar Sandu

Sorry, Room doesn't support auto-creation of tables without data loss.

抱歉,Room 不支持自动创建表而不会丢失数据。

It is mandatory to write the migration. Otherwise, it'll erase all the data and create the new table structure.

必须编写迁移。否则,它将擦除所有数据并创建新的表结构。

回答by Sujeet Kumar

You can do this way-

你可以这样做——

@Database(entities = {User.class, Pet.class}, version = 2)

abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
public abstract Dao getPetDao();
}

Remaining will be same as you have mentioned above-

剩下的和你上面提到的一样——

 db = Room.databaseBuilder(this, AppDatabase::class.java, "your_db")
        .addMigrations(MIGRATION_1_2).build()

Reference - For more

参考 -更多

回答by Larry Stent

You can add the following gradle command to your defaultConfig in your app.gradle:

您可以将以下 gradle 命令添加到 app.gradle 中的 defaultConfig:

javaCompileOptions {
        annotationProcessorOptions {
            arguments = ["room.schemaLocation":
                                 "$projectDir/schemas".toString()]
        }
    }

When you run this it will compile a list of table names with their relevant CREATE TABLE statements from which you can just copy and paste into your migration objects. You might have to change the table names.

当您运行它时,它将编译一个表名称列表及其相关的 CREATE TABLE 语句,您可以从中复制并粘贴到您的迁移对象中。您可能需要更改表名。

For example this is from my generated schema:

例如,这是来自我生成的架构:

"tableName": "assets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` INTEGER NOT NULL, `type` INTEGER NOT NULL, `base` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`asset_id`))"

And so I copy paste the createSql statement and change the '${TABLE_NAME}' to 'assets' the table name, and voila auto generated Room create statements.

所以我复制粘贴 createSql 语句并将 '${TABLE_NAME}' 更改为 'assets' 表名,然后自动生成 Room create 语句。

回答by rudicjovan

In this case, you don't need to do a migration, you can call .fallbackToDestructiveMigration() when you are creating database instance.

在这种情况下,您不需要进行迁移,您可以在创建数据库实例时调用 .fallbackToDestructiveMigration() 。

Example:

例子:

    instance = Room.databaseBuilder(context, AppDatabase.class, "database name").fallbackToDestructiveMigration().build();

And don't forget to change database version.

并且不要忘记更改数据库版本。

回答by user1730694

Maybe in this case(if you've only created new table without changing others) you can do this not creating any migrations at all?

也许在这种情况下(如果您只创建了新表而不更改其他表),您可以这样做根本不创建任何迁移?