Lingo
Lingo

Reputation: 600

Dagger 2 get own Room instance

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.

Upvotes: 5

Views: 3906

Answers (2)

Md. Yamin Mollah
Md. Yamin Mollah

Reputation: 1801

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.

Upvotes: 9

Malik
Malik

Reputation: 5053

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()
    }
}

Upvotes: 5

Related Questions