【问题标题】:Room Database force OnCreate callback房间数据库强制 OnCreate 回调
【发布时间】:2018-06-25 03:26:47
【问题描述】:

我正在使用 RoomDatabase 开发一个需要预先填充其数据的应用程序;我已经设法通过添加onCreate() 回调来做到这一点,但只有在第一次访问数据库时才会调用它(比如调用一个 Daos 函数)。

有什么方法可以强制创建数据库而不进行任何读写操作?

这是我的代码,MyDatabase.get()App.onCreate() 中被调用

@Database(entities = {Entity1.class, Entity2.class}, version = 1, exportSchema = true)
public abstract class MyDatabase extends RoomDatabase {

    private static MyDatabase sInstance;

    public synchronized static TaxCodeDatabase get(Context context) {
        if (sInstance == null) {
            sInstance = buildDatabase(context);
        }
        return sInstance;
    }

    private static MyCodeDatabase buildDatabase(final Context context) {
        return Room.databaseBuilder(context,
                MyCodeDatabase.class,
                "my-database")
                .addCallback(new Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        super.onCreate(db);
                        sInstance.preFillData(context);
                      });
                    }
                })
                .build();
    }

    public abstract Entity1Dao entity2Dao();

    public abstract Entity2Dao entity1Dao();

    /**
     * Populates the database with a series of initial data
     *
     * @param aContext
     */
    public void prePopulateData(Context aContext) {
        //Populate database here
    }

【问题讨论】:

  • 为什么不创建Application 的子类并在该部分调用您的MyDatabase 单例?注意 onCreate 只会被调用一次,除非你删除你的数据库。
  • 因为在一个单独的模块中,我不想在其他地方有另一个实例

标签: java android android-room


【解决方案1】:

我花了一段时间才明白为什么在数据库构建器上调用 .build() 后,Room 数据库没有填充初始数据。

就我而言,迁移和回调仅在真正的读/写操作时触发,这确实违反直觉。实际上是由 Room 使用 class RoomOpenHelper 的原因引起的,如文档中所述:

在打开数据库之前保存对配置的引用的打开助手。

因此,配置和所有回调都存储在RoomOpenHelper 类的实例中,而Sqlite 的SQLiteOpenHelper 实际执行数据库迁移,而不是创建(Java 中的延迟类加载)。

为克服此行为,应执行任何导致getWritableDatabase() 的操作。我最终采用了以下方法:

RoomDatabase db = Room.databaseBuilder(context,
            ...)
            .build();

  // and then
db.beginTransaction()
db.endTransaction()

  // or query a dummy select statement
db.query("select 1", null)

return db

之后,数据库将被创建并填充初始数据。

【讨论】:

  • 如何在资产强制室中使用已填充的数据库来删除以前的数据库,然后从资产 bcz fallbackToDestructiveMigration 执行此操作,但是对于空数据库,我想重新创建它,然后从资产复制数据库表谢谢!
【解决方案2】:

有什么方法可以强制创建数据库而不进行任何读写操作?

不,抱歉。

不过,没有什么能阻止您在调用 Room 之前将预填充的数据库复制到适当的位置。您需要确保预填充的数据库中包含 Room 的元数据(例如,通过使用 Room 本身创建该数据库)。

【讨论】:

  • 谢谢,就像我猜的那样。我会试试你的建议
  • @CommonsWare 他的代码不会是 super.onCreate(db); sInstance.preFillData(context);触发 onCreate() 的递归调用?避免这种情况的唯一方法是:1)从单独的线程调用 sInstance.preFillData(context) 或(2)像你的书中那样对 db 对象使用原始 SQL 查询(但我们失去了 Room 的类型安全性)。是真的如此还是您有更好的预填充建议
  • @Ewoks:单独的线程在这里会非常危险。相反,您可以在回调之外执行preFillData(),但在buildDatabase() 内部执行,其中preFillData() 还会检查数据之前是否已加载。
【解决方案3】:

Room version 2.2.0 (October 2019) 引入了数据库预填充的内置方法:

  1. 来自您应该放在assets/ 目录中的预打包数据库文件 (*.db)。
  2. 来自预打包的数据库文件 (*.db),您应该将其放在除 assets/ 目录之外的某个位置。

更多详情可以阅读official documentation

要获得答案 - 如何准备预打包的数据库,您可以阅读this article in SO

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    相关资源
    最近更新 更多