Using Room, I load my database from assets (an already populated database).
I do plan to update the database content very often, so every time the version is increased, the old database the user has installed in their storage must be removed and generated again (by simply copying the new database into the storage).
This process is supposed to be done automatically by Room when fallbackToDestructiveMigration() is enabled. However, I've found that when enabling fallbackToDestructiveMigration() along with createFromAsset(), every time the database is requested, Room deletes the old database even if it's version is already the latest and generates it again as It would do if a migration was required.
@Module
@InstallIn(SingletonComponent.class)
public class TestRoomModule {
private static final String DATABASE_NAME = "test.db";
@Provides
public TestDatabase provideDatabase(@ApplicationContext Context context) {
return Room.databaseBuilder(
context.getApplicationContext(),
TestDatabase.class,
DATABASE_NAME)
.createFromAsset("databases/test.db")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build();
}
}
Like : this issue mentioned here
The solution proposed by the previous post is simply remove "fallbackToDestructiveMigration" but this is not a solution just a workaround since If I update my Database and my Database version, the app will throw an exception since no migration were provided.
@Database(entities = {
version = 1)
public abstract class TestDatabase extends RoomDatabase {
Updating to version 2:
@Database(entities = {
version = 2)
public abstract class TestDatabase extends RoomDatabase {
Will throw:
A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase
How can I fix this so the fallbackToDestructiveMigration() is triggered ONLY when the database version has actually changed?
Removing
fallbackToDestructiveMigrationwill solve the problem of rewriting/recreating the database on each request.But now you won't be able to do a database migration in the next release because you don't specify a migration plan with
addMigrations(), otherwise you'll getIllegalStateException.Trying to catch that exception will fail as
Roomwon't allow that, something like:So, as you don't have migration plans with
addMigrations(), the only way is to manipulate using thefallbackToDestructiveMigrationby some flag, so we callfallbackToDestructiveMigrationconditionally upon the need.A possible solution is to control that with two integers, one indicates the current database version, and the other indicates the new database version. Either
SharedPrefsorDataStoreare typical tools for that:Assuming the current database version is 1, and in the new version is 2:
Create
SharedPrefshelper methods:Then check the conditional in the Database class:
And whenever you have a release that requires a database migration, you have to set the new database version to the new version, it'd be in the activity
onCreate()callback:Make sure to set that before accessing the database.
If there is no migration required in some release, keep the
setNewDBVersion(this, 1)with the current release.Another Approach for conditional calling of the
fallbackToDestructiveMigration()Instead of maintaining database versions in some storage (SharedPrefs/DataStore), we would check the database version:
Quote from this answer
And that answer referenced the below method for that purpose:
Side Notes:
allowMainThreadQueries()should not be used in a production app, just can be used for testing and simplicity purpose.Personally, I'd prefer to add database migrations instead of using
fallbackToDestructiveMigration()even for prepopulated databases; probably as the later is destructive :).If there are no schema (tables/columns) changes; i.e., just need to update the database entries (rows); we can just have an empty migration plan, and just delete the database on any upgrade with
deleteDatabase()on Context to refresh the database in the assets: