Posted by on 12th December 2018

Scenario 1: keep the database version unchanged — app crashes

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you’ve changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

Scenario 2: version increased, but no migration provided — app crashes

java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.

Scenario 3: version increased, fallback to destructive migration enabled — database is cleared

If you don’t want to provide migrations and you specifically want your database to be cleared when you upgrade the version, call fallbackToDestructiveMigration in the database builder:

database = Room.databaseBuilder(context.getApplicationContext()
,UsersDatabase.class, "Sample.db")
               		.fallbackToDestructiveMigration()
               		.build();

Scenario 4: version increased, migration provided — data is kept

To keep the user’s data, we need to implement a migration. Since the schema doesn’t change, we just need to provide an empty migration implementation and tell Room to use it.

@Database(entities = {User.class},version = 2)
public abstract class UsersDatabase extends RoomDatabase {
…
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
   @Override
   public void migrate(SupportSQLiteDatabase database) {
       // Since we didn't alter the table, there's nothing else to do here.
   }
};
…
database =  Room.databaseBuilder(context.getApplicationContext(),
       UsersDatabase.class, "Sample.db")
       .addMigrations(MIGRATION_1_2)
       .build();

Migration with simple schema changes

Let’s add another column: last_update, to our users table, by modifying the User class. In the UsersDatabase class we need to do the following changes:

1. Increase the version to 3

@Database(entities = {User.class}, version = 3)

public abstract class UsersDatabase extends RoomDatabase

2. Add a Migration from version 2 to version 3

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
   @Override
   public void migrate(SupportSQLiteDatabase database) {
       database.execSQL("ALTER TABLE users "
               + " ADD COLUMN last_update INTEGER");
   }
};

3. Add the migration to the Room database builder:

database = Room.databaseBuilder(context.getApplicationContext(),
       UsersDatabase.class, "Sample.db")
       .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
       .build();

Migrations with complex schema changes

SQLite’s ALTER TABLE… command is quite limited. For example, changing the id of the user from an int to a String takes several steps:

  • create a new temporary table with the new schema,
  • copy the data from the users table to the temporary table,
  • drop the users table
  • rename the temporary table to users

Using Room, the Migration implementation looks like this:

static final Migration MIGRATION_3_4 = new Migration(3, 4) {
   @Override
   public void migrate(SupportSQLiteDatabase database) {
       // Create the new table
       database.execSQL(
               "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");

// Copy the data
       database.execSQL(
               "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");

// Remove the old table
       database.execSQL("DROP TABLE users");

// Change the table name to the correct one
       database.execSQL("ALTER TABLE users_new RENAME TO users");
   }
};

Multiple database version increments

What if your users have an old version of your app, running database version 1, and want to upgrade to version 4? So far, we have defined the following migrations: version 1 to 2, version 2 to 3, version 3 to 4, so Room will trigger all migrations, one after another.

Room can handle more than one version increment: we can define a migration that goes from version 1 to 4 in a single step, making the migration process faster.

static final Migration MIGRATION_1_4 = new Migration(1, 4) {
   @Override
   public void migrate(SupportSQLiteDatabase database) {
       // Create the new table
       database.execSQL(
               "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");
       // Copy the data
       database.execSQL(
               "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");

// Remove the old table
       database.execSQL("DROP TABLE users");

// Change the table name to the correct one
       database.execSQL("ALTER TABLE users_new RENAME TO users");
   }
};

Next, we just add it to the list of migrations:

database = Room.databaseBuilder(context.getApplicationContext(),
       UsersDatabase.class, "Sample.db")
       .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4)
       .build();

Note that the queries you write in the Migration.migrate implementation are not compiled at run time, unlike the queries from your DAOs. Make sure that you’re implementing tests for your migrations.

Reference:

Please follow and like us:

Comments

Be the first to comment.

Leave a Reply


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*


Enjoy this blog? Please spread the word :)