5

I want to add a callback to the room database to populate initial data.

@Provides
@Singleton
fun provideRoom(context: Context): MyRoomDatabase {
    return Room.databaseBuilder(context, MyRoomDatabase::class.java, "my_database")
        .fallbackToDestructiveMigration()
        .addCallback(object : RoomDatabase.Callback() {
            @Override
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onCreate(db)

            }
        })
        .build()
}

For that i need the database instance in the callback to access DAO for inserting data. How does this work?

EDIT:

What I want to achieve: Create initial data for the room database at the app installation

My Callback Class:

class RoomCallback(
 var myRoomDatabase : MyRoomDatabase
)  : RoomDatabase.Callback()  {
override fun onCreate(db: SupportSQLiteDatabase) {
    myRoomDatabase.basicItemDao().insertList(
        listOf(
            BasicItem(),
            BasicItem()
        )
    )
}

}

How i provide the RoomDatabase and the RoomCallback:

@Provides
@Singleton
fun provideRoom(context: Context, roomCallback: RoomCallback): MyRoomDatabase {
    return Room.databaseBuilder(context, MyRoomDatabase::class.java, "my_database")
        .fallbackToDestructiveMigration()
        .addCallback(roomCallback)    
        .build()
}

@Provides
@Singleton
fun provideRoomCallback(myRoomDatabase: MyRoomDatabase): RoomCallback {
    return RoomCallback(myRoomDatabase)
}

PROBLEM: - The RoomCallback and RoomDatabase instance need both the other instance.

Lingo
  • 580
  • 2
  • 7
  • 26
  • I think you can create an Object(Singleton) that extends RoomDatabase.Callback() inside your base package, that does the insertion, and then pass that object inside addCallback() – Debanjan Sep 17 '19 at 06:20
  • @Debanjan I created a `RoomCallback` class and implemented RoomDatabase.Callback(). Now i wanted to provide this `RoomCallback` class with dagger but this class need the MyRoomDatabase Class. And the MyRoomDatabase class needs the RoomCallback class. How do I solve this? – Lingo Sep 17 '19 at 15:43
  • @Lingo does my answer help? – Maddy Sep 23 '19 at 04:53
  • @Maddy No Sorry, I know how to use Dagger2 in combination with Room. But at the part with populating data at the beginning I`ve problems. – Lingo Sep 23 '19 at 06:02
  • @Lingo Can you edit your question with more details of your problem? – Maddy Sep 23 '19 at 06:03

2 Answers2

9

UPDATE: Using Kotlin Coroutine and Dagger2

Late in the party but for future readers, it's very easy to prepopulate your database at creation time or openning time. Make sure you have already added dependency in the gradle file for Coroutine. First create your database like:

    /**
     * Main database.
     */
    @Database(
        entities = [
            Login::class],
        version = 1,
        exportSchema = false
    )
    abstract class AppDatabase : RoomDatabase() {
        abstract fun loginDao(): LoginDao

        companion object {
            
            @Volatile private var INSTANCE: AppDatabase? = null

            fun getInstance(app: Application): AppDatabase = INSTANCE ?: synchronized(this) {
            INSTANCE ?: buildDatabase(app).also { INSTANCE = it }
        }

        private fun buildDatabase(app: Application) =
            Room.databaseBuilder(app,
                AppDatabase::class.java,
                "your database name")
            .addCallback(object : Callback() {
                // Pre-populate the database after onCreate has been called. If you want to prepopulate at opening time then override onOpen function instead of onCreate.
                override fun onCreate(db: SupportSQLiteDatabase) {
                    super.onCreate(db)
                    // Do database operations through coroutine or any background thread
                    val handler = CoroutineExceptionHandler { _, exception ->
                        println("Caught during database creation --> $exception")
                    }

                    CoroutineScope(Dispatchers.IO).launch(handler) {
                        prePopulateAppDatabase(getInstance(app).loginDao())
                    }
                }
            })
            .build()

        suspend fun prePopulateAppDatabase(loginDao: LoginDao) {
            val admin = Login(0, "Administrator", "1234", 1, isActive = true, isAdmin = true, isLogged = false)
            loginDao.insertLoginData(admin)
        }
    }
}

Then you can provide singleton instance by placing below code to your dagger AppModule or in a separate database module as you wish.

@Singleton
@Provides
fun provideDb(app: Application): AppDatabase {
    return AppDatabase.getInstance(app)
}

@Singleton
@Provides
fun provideLoginDao(db: AppDatabase): LoginDao {
    return db.loginDao()
}

that's it, you are done. Inject anywhere your singleton database object like:

@Inject lateinit var loginDao: LoginDao

then use it.

Md. Yamin Mollah
  • 1,609
  • 13
  • 26
5

Setup a database first

@Database(
    entities = [User::class],
    version = VERSION_CODE
)
abstract class DatabaseManager : RoomDatabase() {
    abstract fun userDao(): UserDao
}

Now create a DatabaseModule

@Module
class DatabaseModule {

    @Singleton
    @Provides
    fun provideRoomDatabase(@ApplicationContext context: Context): RoomDatabase {
        return Room.databaseBuilder(context, RoomDatabase::class.java, "dbName")
            .setJournalMode(JournalMode.TRUNCATE)
            .build()
    }
}

You can create a separate module or add a method in DatabaseModule it self providing dao object. Say for example I have a UserDao then

@Module
class UserModule {

    @Singleton
    @Provides
    fun provideDao(database: DatabaseManager): UserDao {
        return database.userDao()
    }
}
Maddy
  • 4,525
  • 4
  • 33
  • 53