Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/391.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 仅添加新表时的文件室数据库迁移_Java_Android_Database Migration_Android Room - Fatal编程技术网

Java 仅添加新表时的文件室数据库迁移

Java 仅添加新表时的文件室数据库迁移,java,android,database-migration,android-room,Java,Android,Database Migration,Android 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 clas

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

@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:需要从1迁移到2。

假设我没有更改
用户
类(因此所有数据都是安全的),我必须提供迁移,只创建一个新表。因此,我正在研究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:我没有接触任何现有的表,所以数据是安全的。请为我创建迁移?

在这种情况下(如果您只创建了新表而没有更改其他表),您可以不创建任何迁移来完成此操作?

对不起,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()

引用-

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

例如:

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

别忘了更改数据库版本

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

javaCompileOptions {
        annotationProcessorOptions {
            arguments = ["room.schemaLocation":
                                 "$projectDir/schemas".toString()]
        }
    }
运行此命令时,它将编译一个表名列表及其相关的CREATETABLE语句,您可以从中复制并粘贴到迁移对象中。您可能必须更改表名

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

"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}”更改为“assets”表名,瞧,自动生成了Room create语句。

Room没有良好的迁移系统,至少在
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
,则文件室将在项目文件夹的/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
,其中包含房间数据库中的所有查询

  "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\u 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\u Impl
文件,并使用您添加的注释处理

kapt "androidx.room:room-compiler:$room_version"
这意味着您也可以这样做,并创建自己的注释处理库,该库为您生成所有必要的create查询

其想法是为
@实体
@数据库
的房间注释创建一个注释处理库。以一个用
@Entity
注释的类为例。这些是您必须遵循的步骤

  • 创建一个新的
    StringBuilder
    并附加“如果不存在创建表”
  • class.simplename
    或通过
    @Entity
    tableName
    字段获取表名。将其添加到您的
    StringBuilder
  • 然后为类的每个字段创建SQL列。通过字段本身或
    @ColumnInfo
    注释获取字段的名称、类型和可空性。 对于每个字段,必须将列的
    id INTEGER NOT NULL
    样式添加到
    StringBuilder
  • 通过
    @PrimaryKey
  • 添加
    ForeignKey
    索引(如果存在)
  • 完成后,将其转换为字符串并保存在您想要使用的某个新类中。例如,如下面所示保存它
  • 然后,你可以把它当作

    val MIGRATION_1_2 = object : Migration(1, 2){
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL(UserSqlUtils().createTable)
        }
    }
    
    我为自己制作了这样一个库,你可以查看它,甚至可以在你的项目中使用它。请注意,我创建的库没有满,它只满足了我创建表的要求

    希望它有用

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

    2.2.0


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

    你找到解决这个问题的方法了吗?我也有同样的问题,并用你的方法解决了它,但也没有找到解决方法。很高兴我不是一个人彼此彼此。我发现很不方便,room能够在数据库的内部生成create查询,但不能只创建表,一旦迁移开始……我会为之付出很多
    val MIGRATION_1_2 = object : Migration(1, 2){
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL(UserSqlUtils().createTable)
        }
    }