通过Dagger 2提供RoomDatabase时,实现.addCallback()的正确方法是什么?

时间:2018-05-25 02:54:12

标签: android kotlin dagger-2 android-room

我正在使用Dagger 2在我的应用中创建并共享我的RoomDatabase。

我正在尝试实现addCallback(),因此我可以覆盖数据库的onCreate()函数并使用它来插入我的初始数据库值。这就是我遇到问题的地方。

我觉得我必须忽略一些显而易见的事情,但我无法找到一种优雅地做到这一点的方法。

RoomDatabase类:

@Database(
        entities = [Station::class],
        version = 1,
        exportSchema = false
)
abstract class TrainDB : RoomDatabase() {

    abstract fun stationDao() : StationDao

} 

DAO:

@Dao
abstract class StationDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun insert(stations: Station)

    @Query("SELECT * FROM station_table")
    abstract fun getAll() : LiveData<List<Station>>

}

Dagger模块:

@Module
class DataModule {

    @Singleton
    @Provides
    fun provideDb(app: Application): TrainDB {
        var trainDB: TrainDB? = null
        trainDB = Room
                .databaseBuilder(app, TrainDB::class.java, "train.db")
                .allowMainThreadQueries()
                .fallbackToDestructiveMigration()
                .addCallback(object : RoomDatabase.Callback() {
                    override fun onCreate(db: SupportSQLiteDatabase) {
                        super.onCreate(db)

                        /*
                        WHAT GOES HERE?
                        */

                    }
                })
                .build()
        return trainDB
    }

    @Singleton
    @Provides
    fun providesStationDao(db: TrainDB) : StationDao = db.stationDao()

}

我希望能够在onCreate()回调中访问我的DAO。很明显这应该是可能的,因为谷歌正在将Room和Dagger推到一起,这可能是一个非常常见的用例。

我尝试将DAO作为provideDB()的构造函数参数提供,但这会创建循环依赖

我尝试将RoomDatabase初始化为伴侣对象。然后,我可以调用可以访问DAO的Room.builder方法,而不是在provideDB()方法中使用getInstance()格式。但是这种方式我遇到了一个错误的递归调用getWriteableDatabase()

我知道我可以使用像db.execSQL()这样的东西,但是当我使用Room时,这样做真是太可惜了。

有没有更好的方法让我失踪?我使用Kotlin,但欢迎使用Java示例。 :)

3 个答案:

答案 0 :(得分:0)

您可以创建最终的单元素数组。

@AppScope
@Provides
public AppDatabase appDatabase(@ApplicationContext Context appContext, AppExecutors executors) {

    final AppDatabase[] databases = new AppDatabase[1];

    databases[0] = Room.databaseBuilder(appContext, AppDatabase.class, AppDatabase.DATABASE_NAME)
            .fallbackToDestructiveMigration()
            .addCallback(new RoomDatabase.Callback() {
                @Override
                public void onCreate(@NonNull SupportSQLiteDatabase db) {
                    super.onCreate(db);
                    executors.diskIO().execute(() -> {
                        databases[0].populateDatabaseOnCreate();
                    });
                }
            }).build();
    return databases[0];
}

答案 1 :(得分:0)

我已经这样管理了它:

@Module
class DataModule {

lateinit var trainDB: TrainDB

@Singleton
@Provides
fun provideDb(app: Application): TrainDB {
    trainDB = Room
            .databaseBuilder(app, TrainDB::class.java, "train.db")
            .allowMainThreadQueries()
            .fallbackToDestructiveMigration()
            .addCallback(object : RoomDatabase.Callback() {
                override fun onCreate(db: SupportSQLiteDatabase) {
                    super.onCreate(db)

                    /*

                    trainDB.stationDao().insert(...)


                    */

                }
            })
            .build()
    return trainDB
}

@Singleton
@Provides
fun providesStationDao(db: TrainDB) : StationDao = db.stationDao()

}

但是请记住,您需要从数据库中进行虚假读取,以启动db并调用onCreate()。未创建db时,请勿在第一次交互时写入db,因为这会创建竞争条件,并且您在创建时的写入不会生效。

答案 2 :(得分:-1)

您可以在java中执行此操作

    AppDatabase appDatabase = null;

    AppDatabase finalAppDatabase = appDatabase;
    appDatabase = Room.databaseBuilder(MyApplication.getApplication(),
            AppDatabase.class, Constants.DATABASE_NAME).
            addCallback(new RoomDatabase.Callback() {
                @Override
                public void onCreate(@NonNull SupportSQLiteDatabase db) {
                    super.onCreate(db);
                    //check for null
                    finalAppDatabase.yourDao();
                }
            }).
            build();
     return appDatabase;