Android Architecture Components: Room — Migration

This post was published in Android Weekly #285 issue

Hi! This is the last post in the series of Architecture Components articles. This time I’d like to present you migration of a Room database.

If you haven’t read previous articles, you can do it here:

Android Architecture Components: Room — Introduction

Android Architecture Components: Room — Relationships

Android Architecture Components: Room — Custom Types

Either way, I encourage you to read todays post. Let’s start!

Migration

Sometimes there’a s need to change our existing database schema. If we’ll add, update or delete some fields in the database and then run our application, we’ll see exception from Room:

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.

Room will discover that it’s schema has changed and it‘s no more consistent with database version. Now if we’ll increment version number and run application once again, we’ll see another exception:

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.

Now Room doesn’t know how to migrate database from version 1 to version 2. In this error Room suggest us two solutions:

  • drop and recreate the whole database
  • upgrade existing database schema to the newer version

First option is the easiest one, but it’s not the best for the users, as we’ll clear the whole database…

Despite I don’t recommend this solution, it’s possible with Room. To do it we need to add one line to the database builder:

Room.databaseBuilder(context, RepoDatabase.class, DB_NAME)
.fallbackToDestructiveMigration()
.build();

But, If we’d like to have our application more user-friendly, we can provide migration of our database. With Room it’s also quite easy. To do it we need to declare migration in database builder:

Room.databaseBuilder(context, RepoDatabase.class, DB_NAME)
.addMigrations(FROM_1_TO_2)
.build();
static final Migration FROM_1_TO_2 = new Migration(1, 2) {
@Override
public void migrate(final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Repo
ADD COLUMN createdAt TEXT");
}
};

To the Migration object we simply pass version numbers and SQL statement to make a migration.

Now, something what’s the biggest advantage of Room, auto-generation of SQL statements, is unfortunately something what may bother us upon database migration as we don’t have proper statements. Luckily, there’s a solution for that:

android {
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
}

Thanks to this code fragment added to build.gradle file we’ll be able to get those auto-generated by Room SQL statements and use them to make a migration:

"tableName": "Repo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `url` TEXT, PRIMARY KEY(`id`))"

Files are located accordingly in app/schemas/your.package.name.RepoDatabase/ directory.

Conclusion

That’s all! I hope you liked that post. If you did — don’t forget to 👏 !

Flutter GDE / Android & Flutter Developer / blogger / speaker / cat owner / travel enthusiast

The (retired) Pub(lication) for Android & Tech, focused on Development