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

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

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

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

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

当然,Room 抛出了一个例外: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

假设我没有更改 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`))");
}
};

但是我发现手动操作很不方便。 有没有办法告诉 Room: < em > 我没有触摸任何现有的表,所以数据是安全的。请为我创建迁移?

42279 次浏览

也许在这种情况下(如果您只创建了新表而没有更改其他表) ,您可以完全不创建任何迁移吗?

对不起,Room 不支持在不丢失数据的情况下自动创建表。

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

你可以这样做-

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


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

剩下的将与你上面提到的一样-

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

参考资料 -为了更多

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

例如:

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

别忘了更改数据库版本。

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

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

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

例如,这来自我生成的模式:

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

因此,我复制粘贴 createSql 语句,并将“ ${ TABLE _ NAME }”更改为“ asset”表名,瞧,自动生成了 Room create 语句。

房间 没有有一个良好的迁移系统,至少在 2.1.0-alpha03之前没有。

因此,在我们有更好的迁移系统之前,有一些变通方法可以使房间里的迁移更容易。

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

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种方法

1. 自己写作

基本上,您必须编写与 Room 生成的脚本相匹配的上述脚本。这种方法可行,但不可行。(考虑到你有50个字段)

2. 导出模式

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

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

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

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

当您运行或构建项目时,您将得到一个 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
},

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

3. 从 AppDatabase _ Impl 获取查询

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

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

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

4. 注释处理。

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

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

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

其思想是为 @Entity@Database的 Room 注释建立一个注释处理库。以一个带有 @Entity注释的类为例。以下是您必须遵循的步骤

  1. 创建一个新的 StringBuilder并附加“如果不存在则创建表”
  2. class.simplename@EntitytableName字段中获取表名,并将其添加到 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))";
}

然后,你可以用它作为

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

我为自己做了这样一个图书馆,你可以检查,甚至在你的项目中使用它。注意,我创建的库并没有满,它只是满足了我创建表的要求。

房间扩展以便更好地迁移

应用程序,使用 Room

希望有用。

更新

在写这个答案的时候,房间版本是 2.1.0-alpha03,当我给开发人员发邮件的时候,我得到了一个回复

预计在 2.2.0中将有更好的迁移系统

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

对于那些仍在寻找这个问题解决方案的人,我有一些好消息。Room 开始支持2.4.0版本的自动迁移

Https://developer.android.com/jetpack/androidx/releases/room#2.4.0-alpha01