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

@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确实NOT有一个好的迁移系统,至少要到2.1.0-alpha03.

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

由于没有像@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,文件室将在项目文件夹的/schemas中生成数据库模式.用法是

@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会在编译时间内生成上述schema个和AppDatabase_Impl个文件,并使用您添加的注释处理

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

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

我们的 idea 是为@Entity@Database的房间注释创建一个注释处理库.以一个用@Entity注释的类为例.以下是您必须遵循的步骤

  1. 创建一个新的StringBuilder并附加"如果不存在创建表"
  2. class.simplename@EntitytableName字段中获取表名.把它加到你的StringBuilder
  3. 然后为类的每个字段创建SQL列.通过字段本身或@ColumnInfo注释获取字段的名称、类型和可空性.
  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)
    }
}

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

RoomExtension for better Migration

Application that uses RoomExtension

希望它能派上用场.

使现代化

在 compose 本文时,房间版本是2.1.0-alpha03,当我给开发人员发邮箱时,我得到的回复是

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

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

Android相关问答推荐

ArrayList.remove()(Kotlin中的removeAt())导致奇怪的IndexOutOfBoundsResponse异常

如何在Android中使用TextView设置动态文本的样式

当X为lambda函数时,如何避免Android Studio错误检测参数X未使用?

Android compose ,在图像中zoom 而不裁剪?

Jetpack Compose-如何使用值动画直接控制其他动画

有人能帮我在应用程序上使用模拟位置时避免被发现吗?我已经反编译并粘贴了一个代码,S小文件

从未设置实时数据值

将DiffUtils用于Android上的Recrecerview适配器

如何解决Gradle构建错误:java.lang.NoSuchMethodError

如何在Jetpack Compose中将对象的移动从一个路径平滑地切换到另一个路径?

Android kotlin 中闪屏 API 执行完成后如何根据身份验证将用户导航到特定屏幕

如何在 kotlin 中接收带有和不带有可空对象的集合并保持引用相同

Android Studio 在 list 文件中已经声明了活动类,但仍出现无法找到明确的活动类的错误

如何避免多次调用 Jetpack Compose 的 onClick 回调

如何让这个三角形指示器在 android jetpack compose 中旋转和移动?

如何在 React Native 中调试网络响应

在 Jetpack Compose 中重用具有重复代码的列

Jetpack compose 为网络检索视频帧导致延迟

Android Compose 创建抖动动画

关于launchWhenX和repeatOnLifecycle的问题