Migrations
Migrations are used to change existing database schema to a new one. ReActiveAndroid provides an abstraction layer to ease SQLite migrations in the form of the Migration
class. A Migration
class defines the actions that should be performed when migrating from one specific version to another. ReActiveAndroid uses its own implementation of SQLiteOpenHelper
and, in the onUpgrade
method, will trigger the migrations you defined.
Each version of the database schema has a unique identifier in the form of a string, called identity_hash. This identity hash is stored in a special table reactive_master_table. When changing the database schema, the identity hash also changes.
If you change the database schema, you must to increase the database version, and also provide migrations to upgrade the database from one version to another. For example, in our database there is a Notes
table with the following scheme:
@Table(name = "Notes", database = AppDatabase.class)
public class Note extends Model {
@PrimaryKey
private Long id;
@Column(name = "title")
private String title;
@Column(name = "text")
private String text;
}
In the next version of the application, we changed the table scheme:
@Table(name = "Notes", database = AppDatabase.class)
public class Note extends Model {
@PrimaryKey
private Long id;
@Column(name = "title")
private String title;
@Column(name = "text")
private String text;
@Column(name = "color")
private int color;
}
We also need to increase the database version:
@Database(name = "AppDatabase", version = 2)
public class AppDatabase {
In this case, the identity hash of the new database schema will also change. If we do not increase the database version, then ReActiveAndroid will return the following error:
java.lang.IllegalStateException: ReActiveAndroid 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.
ReActiveAndroid compares the current identity hash with the one saved in the reactive_master_table
. If they do not match, ReActiveAndroid throws an exception and asks to increase the database version.
After this, we must provide a migration to upgrade the database from the old scheme to a new one:
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.
}
};
Then add your mugrations to the config:
appDatabaseConfig.addMigrations(MIGRATION_12, MIGRATION_2_3, ...)
A migration can handle more than 1 version (e.g. if you have a faster path to choose when going version 3 to 5 without going to version 4). If Room opens a database at version 3 and latest version is >= 5, Room will use the migration object that can migrate from 3 to 5 instead of 3 to 4 and 4 to 5.
If we do not provide migration, then ReActiveAndroid will throw the following exception:
java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call disableMigrationsChecking in the builder in which case ReActiveAndroid will re-create all of the tables.
So even if you have no changes between 2 versions, you should still provide a Migration object to the builder. If we don't want to create migrations, we can disable the checking:
DatabaseConfig appDatabaseConfig = new DatabaseConfig.Builder(AppDatabase.class)
...
.disableMigrationsChecking()
.build();
In this case, ReActiveAndroid will delete all tables and create them again. This helps identify any possible corruption early on so you can take action quickly instead of finding out a fix is needed after the corruption has grown even larger in scope.
Migrating with files
You can also store your SQL-statements in special.sql
files. Just put all the statements in the file and save it in the assets
folder (we recomment save migration files to assets/migrations/
folder).
For example, we want to create migration from version 1
to version 2
. For this we created file with name 1_2.sql
in assets
folder with following content:
-- single line comment
alter table Note add column is_favorite INTEGER NOT NULL default 0
/*
Multiline comment also supports
*/
create table people (id integer primary key autoincrement, name text, posid integer)
Each SQL-statement must be on a separate line or must be end with ;
.
After that, we must run the migration file as follows:
Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SQLiteDatabase database) {
AssetsSqlMigration.executeSqlScript(database, "migrations/app_database/1_2.sql");
}
};