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
Room database migration if only new table is added
提问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: Pet
and 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 User
class (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 migrate
method.
在你的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 = true
inside your @Database
annotation, 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.grade
of 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 createSql
within you migrate
method.
因此,您可以createSql
在migrate
方法中包含上述内容。
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.java
file. 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 createAllTables
method, there will be the create scripts of all the entities. You can get it and include in within you migrate
method.
在createAllTables
方法中,将有所有实体的创建脚本。您可以获取它并将其包含在您的migrate
方法中。
4. Annotation Processing.
4. 注释处理。
As you might guess, Room generates all of the above mentioned schema
, and AppDatabase_Impl
files within compilation time and with Annotation Processing which you add with
正如您可能猜到的那样,Room 会在编译时间内生成上述所有文件schema
和AppDatabase_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 @Entity
and @Database
. Take a class that is annotated with @Entity
for example. These are the steps you will have to follow
这个想法是为@Entity
和 的房间注释制作一个注释处理库@Database
。以一个带有注释的类@Entity
为例。这些是您必须遵循的步骤
- Make a new
StringBuilder
and append "CREATE TABLE IF NOT EXISTS " - Get the table name either from
class.simplename
or bytableName
field of@Entity
. Add it to yourStringBuilder
- 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
@ColumnInfo
annotation. For every field, you have to addid INTEGER NOT NULL
style of a column to yourStringBuilder
. - Add primary keys by
@PrimaryKey
- Add
ForeignKey
andIndices
if exists. - After finishing convert it to string and save it in some new class that you want to use. For example, save it like below
StringBuilder
创建一个新的并附加“如果不存在则创建表”- 获取表名 from
class.simplename
或 by 的tableName
字段@Entity
。将其添加到您的StringBuilder
- 然后为您班级的每个字段创建 SQL 列。通过字段本身或通过
@ColumnInfo
注释获取字段的名称、类型、可为空性。对于每个字段,您必须将id INTEGER NOT NULL
列样式添加到您的StringBuilder
. - 添加主键
@PrimaryKey
- 添加
ForeignKey
和Indices
如果存在。 - 完成后将其转换为字符串并将其保存在您要使用的某个新类中。例如,保存如下
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
Application that uses RoomExtension
Hope it was useful.
希望它有用。
UPDATE
更新
By the time of writing this answer, room version was 2.1.0-alpha03
and 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?
也许在这种情况下(如果您只创建了新表而不更改其他表),您可以这样做根本不创建任何迁移?