草庐IT

java - 仅添加新表时的房间数据库迁移

coder 2023-05-08 原文

假设,我有一个简单的 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:我没有碰任何现有的表,所以数据是安全的。请为我创建迁移?

最佳答案

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,Room 将在项目文件夹的/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 会在编译时间内生成上述所有 schemaAppDatabase_Impl 文件,并使用您添加的注释处理

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

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

想法是为@Entity@Database的Room注解做一个注解处理库。以使用 @Entity 注释的类为例。这些是您必须遵循的步骤

  1. 创建一个新的 StringBuilder 并附加“CREATE TABLE IF NOT EXISTS”
  2. class.simplename@EntitytableName 字段获取表名。将其添加到您的 StringBuilder
  3. 然后为类的每个字段创建 SQL 列。通过字段本身或 @ColumnInfo 注释获取字段的名称、类型、可空性。 对于每个字段,您必须将列的 id INTEGER NOT NULL 样式添加到您的 StringBuilder
  4. 通过@PrimaryKey
  5. 添加主键
  6. 添加 ForeignKeyIndices(如果存在)。
  7. 完成将其转换为字符串并将其保存在您要使用的某个新类中。例如,如下所示保存
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

希望有用。

更新

在撰写此答案时,房间版本为 2.1.0-alpha03,当我通过电子邮件发送给开发人员时,我收到了回复

It is expected to have better Migration System in 2.2.0

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

关于java - 仅添加新表时的房间数据库迁移,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48399852/

有关java - 仅添加新表时的房间数据库迁移的更多相关文章

  1. ruby-on-rails - Ruby on Rails 迁移,将表更改为 MyISAM - 2

    如何正确创建Rails迁移,以便将表更改为MySQL中的MyISAM?目前是InnoDB。运行原始执行语句会更改表,但它不会更新db/schema.rb,因此当在测试环境中重新创建表时,它会返回到InnoDB并且我的全文搜索失败。我如何着手更改/添加迁移,以便将现有表修改为MyISAM并更新schema.rb,以便我的数据库和相应的测试数据库得到相应更新? 最佳答案 我没有找到执行此操作的好方法。您可以像有人建议的那样更改您的schema.rb,然后运行:rakedb:schema:load,但是,这将覆盖您的数据。我的做法是(假设

  2. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  5. ruby - 将 Bootstrap Less 添加到 Sinatra - 2

    我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它

  6. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  7. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  8. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  9. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  10. ruby - 可以通过多少种方法将方法添加到 ruby​​ 对象? - 2

    当谈到运行时自省(introspection)和动态代码生成时,我认为ruby​​没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby​​的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资

随机推荐