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
@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)
}
}