I'm using Dagger 2 to create and share my RoomDatabase where necessary in my app.
I'm trying to implement addCallback()
so I can override the database's onCreate()
function and use it to insert my initial database values. This is where I'm running into issues.
I feel like I have to be overlooking something obvious, but I can't figure out a way to do this gracefully.
RoomDatabase class:
@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:
@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()
}
I'd like to be able to access my DAO in the onCreate()
callback. It seems obvious that this should be possible because Google is pushing Room and Dagger together and this is likely a pretty common use case.
I've tried providing the DAO as a constructor argument for provideDB()
, but that creates a circular dependency
I've tried initializing my RoomDatabase as a companion object. Then instead of using the Room.builder
format in my provideDB()
method, I can call a getInstance()
method that has access to the DAO. But this way I'm met with an error for a recursive call to getWriteableDatabase()
.
I understand that I can use something like db.execSQL()
, but it seems like such a shame to do that when I'm using Room.
Is there a better way that I'm missing? I'm using Kotlin, but Java examples are welcome. :)
You can create final one-element array.
@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];
}
I have managed it like this:
@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()
}
But remember You need to to do a fake read from the database in order to initiate the db and invoke onCreate(). don't write into db as your first interact when db hasn't been created because it will create a race condition and your on create writing won't take effect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With