How to Use Room Persistence Library with Pre-Populated Database

How to use Room Persistence Library with pre-populated database?

This is how I solved it, and how you can ship your application with a pre-populated database (up to Room v. alpha5)

  • put your SQLite DB database_name.db into the assets/databases folder

  • take the files from this repo and put them in a package called i.e. sqlAsset

  • in your AppDatabase class, modify your Room's DB creation code accordingly:

    Room.databaseBuilder(context.getApplicationContext(), 
    AppDatabase.class,
    "database_name.db")
    .openHelperFactory(new AssetSQLiteOpenHelperFactory())
    .allowMainThreadQueries()
    .build();

Note that you have to use "database_name.db" and not getDatabasePath() or other methods: it just needs the name of the file.

How to pre-populate the room database with some data under hilt viewModel?

I tried to write it with the previous idea, roughly, and it works successfully, but I don't know if anything is incorrect, so please let me know if it is!

@Database(entities = [Puzzle::class], version = 1, exportSchema = false)
abstract class PuzzleDatabase : RoomDatabase() {
abstract fun getPuzzleDao() : PuzzleDao
}

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

@Volatile
private var INSTANCE: PuzzleDatabase? = null

private class PuzzleDatabaseCallback(
private val scope: CoroutineScope
) : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
INSTANCE?.let { /*database ->*/
scope.launch {
// you can pre-populate some data here
// database.getPuzzleDao().insert(Puzzle(0, 90000L, 2L, Puzzles.TWO))
}
}
}
}

@Singleton
@Provides
fun puzzleDataBase(
@ApplicationContext app: Context
) : PuzzleDatabase {
return INSTANCE ?: synchronized(this) {
val scope = CoroutineScope(Dispatchers.IO)
val instance = Room.databaseBuilder(
app,
PuzzleDatabase::class.java,
"puzzle_database"
) .addCallback(PuzzleDatabaseCallback(scope))
.build()
.also { INSTANCE = it }
instance
}
}

@Singleton
@Provides
fun getDao(db: PuzzleDatabase) = db.getPuzzleDao()

}

repository.kt

class PuzzleRepository @Inject constructor (
val puzzleDao: PuzzleDao
) {
val all = puzzleDao.getAll()
}

Pre-populating Room database with Hilt without creating an extra instance of the database

Your provideDatabase method always creates a new instance whenever it is called: Dagger makes it a singleton by only calling that method once. The only way to get the singleton GameDatabase instance managed by ApplicationComponent is to request it as a dependency. Since GameDatabase will need to depend on itself via GameDao, this is a circular dependency.

To resolve a circular dependency in Dagger, you can depend on a Provider or Lazy:

    @Singleton
@Provides
fun provideDatabase(@ApplicationContext context: Context, gameDaoProvider: Provider<GameDao>): GameDatabase {
return Room.databaseBuilder(context, GameDatabase::class.java, GameDatabase.GAME_DB_NAME)
.addCallback(
object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// Initialize the database with the first game
ioThread {
gameDaoProvider.get().createNewGame(Game())
}
}

override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)

// Ensure there is always one game in the database
// This will capture the case of the app storage
// being cleared
// This uses the existing instance, so the DB won't leak
ioThread {
val gameDao = gameDaoProvider.get()

if (gameDao.gameCount() == 0) {
gameDao.createNewGame(Game())
}
}
}
}
).build()
}

Cannot pre-populate room database when changing schema

After a long time I realised the problem.

I thought I did not need migration as I only wanted to change the pre-populating database, and not migrate anything currently on the device. However the use of destructive migration also deletes your pre-populating data when you add one to the schema number.

So simply add an empty migration and it works

val migration1: Migration = object : Migration(5, 6) { override fun migrate(database: SupportSQLiteDatabase) {}}

Thanks very much to myself for sorting my problem.

Android Room Pre-populated Data not visible first time

Your DAO returns suspend fun getAllUser(): List<User>, meaning it's a one time thing. So when the app starts the first time, the DB initialization is not complete, and you get an empty list because the DB is empty. Running the app the second time, the initialization is complete so you get the data.

How to fix it:

  1. Switch getAllUser() to return a Flow:
// annotations omitted
fun getAllUser(): Flow<List<User>>

  1. Switch insertUser to use a List
// annotations omitted
suspend fun insertUser(users: List<User>)

The reason for this change is reducing the number of times the Flow will emit. Every time the DB changes, the Flow will emit a new list. By inserting a List<User> instead of inserting a single User many times the (on the first run) Flow will emit twice (an empty list + the full list) compared to number of user times with a single insert.

Another way to solve this issue is to use a transaction + insert a single user.


  1. I recommend you use viewModelScope inside the ViewModel to launch coroutines so it's properly canceled when the ViewModel is destroyed.

Using pre-populated database in Room by copy database file from assert

I solved after spending 6 hours on researching and R & D .

Context is that : - I want to put already existing finaldb.db(which is present inside assests folder) into room database .

Step 1 :
copy this framework files from here link

Step 2 :
You need to migrate , chill i have code :)

@Database(entities = {Status.class}, version = 1,exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract DataDao StatusDao();

private static AppDatabase INSTANCE;

public static AppDatabase getDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE = createDatabase(context);
}
return (INSTANCE);
}

private static final Migration MIGRATION_2_3 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
Log.d("kkkk","bc");

String SQL_CREATE_TABLE = "CREATE TABLE IF NOT EXISTS 'Status' " +
"( 'id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
" 'category' TEXT NOT NULL," +
" 'sub_category' TEXT NOT NULL," +
" 'content' TEXT NOT NULL," +
" 'favourite' INTEGER DEFAULT(0))";

database.execSQL(SQL_CREATE_TABLE);

}
};

private static AppDatabase createDatabase(Context context) {
RoomDatabase.Builder<AppDatabase> builder =
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class,
context.getString(R.string.dbase_name));

return (builder.openHelperFactory(new AssetSQLiteOpenHelperFactory())
.allowMainThreadQueries()
.addMigrations(MIGRATION_2_3)
.build());
}

}

In MIGRATION_2_3 you have to create table exactly same as current database(which is present in assests folder)

want to learn about migration

Step 3 :
Now table is created successfully in room database !

In case of crash see your logcat , in which its written in understandable form .



Related Topics



Leave a reply



Submit