When we use Room database in our android project, we need to add or make changes in the database. Imagine that you published your app and the users added their data in the app database. Then, you have upgraded the app database and launched a new update for your project. In this case, the users lose their database. So, it is important to preserve users data that already saved in their devices when you change the database schema. For this reason, we need to understand Room Database Migration in Android Studio.
Note: This article is part of the Room Database series. Which cover all the details about Room Persistence Library.
1- Room Database in Android Studio with Simple To Do List App
Let`s get started
Room Database Migration is very important concept in Android App Development. In many cases, we need to add, remove change datatypes of columns in Room Database entities. Even so, we need to remove or add new tables (entities) in Room Database. So, we have to change the database version. In such case we need to perform Room Database migration in our schema to preserve users data. Room Persistence Library allows us to perform incremental migration to address this need.
Steps of Room Database Migration
I will use the code from the example of the above link. The first step is to add a new column for items table and name it item_importance. The column datatype is String and with default value ‘Low’. You can replace this code by the code from the previous article from the above link.
package com.androidhands.todolistusingroomdatabase.MRoomDatabase;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
import java.sql.Time;
import java.util.Date;
@Entity(tableName = "to_Do_table")
public class ToDoTable {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
private int id;
@ColumnInfo(name = "item")
private String item;
@TypeConverters(DateConverter.class)
@ColumnInfo(name = "date")
private Date date;
@TypeConverters(DateConverter.class)
@ColumnInfo(name = "time")
private Date time;
@ColumnInfo(name = "completed")
private boolean completed;
// the first step of altering the table in this example is to add new column
@ColumnInfo(name = "item_importance")
private String itemImportance;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public String getItemImportance() {
return itemImportance;
}
public void setItemImportance(String itemImportance) {
this.itemImportance = itemImportance;
}
}
The next Step, is to upgrade Room Database Version. From MyRoomDatabase abstract class, upgrade the database version by increasing version value by one.
//upgrade the database version by increasing version value by one
@Database(entities = {ToDoTable.class},version = 2, exportSchema = false)
public abstract class MyRoomDatabase extends RoomDatabase {
public abstract MyDaoInterface myDaoInterface();
public static MyRoomDatabase mInstance;
public static MyRoomDatabase getInstance(Context context){
if (mInstance == null){
mInstance = Room.databaseBuilder(context,MyRoomDatabase.class,"MyDatabaseName")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build();
}
return mInstance;
}
}
Handle column default value when upgrading to Room 2.2.0
Note that from the above class. I have called the fallbackToDestructiveMigration(), because the migration path does not exist. So i have called this method to prevent illegalStateException. In such case it is acceptable to lose the existing users data. But now I will create the migration path to preserve users data. So, remove the call of this method.
In Room 2.2.0 and higher, you can define a default value for a column by using the annotation @ColumnInfo(defaultValue = "...")
. In versions lower than 2.2.0, the only way to define a default value for a column is by defining it directly in an executed SQL statement, which creates a default value that Room does not know about. This means that if a database is originally created by a version of Room lower than 2.2.0, upgrading your app to use Room 2.2.0 might require you to provide a special migration path for existing default values that you defined without using Room APIs.
To ensure that the database schema is consistent across all users when column default values are declared in your earlier migration paths, do the following the first time you upgrade your app to use Room 2.2.0 or higher.
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
/* database.execSQL("CREATE TABLE new_to_Do_table (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"item TEXT," +"date INTEGER, "+"time INTEGER, "+
"completed INTEGER DEFAULT 0, "+
"item_importance TEXT NOT NULL DEFAULT 'Low')");
database.execSQL("INSERT INTO new_to_Do_table (id, item, date, time, completed) " +
"SELECT id, item, date, time, completed FROM to_Do_table");
database.execSQL("DROP TABLE to_Do_table");
database.execSQL("ALTER TABLE new_to_Do_table RENAME TO to_Do_table");*/
}
};
Next, in the database build method call the .addMigrations(MIGRATION_1_2)
, The final code for MyRoomDatabase abstract class should be as the following.
package com.androidhands.todolistusingroomdatabase.MRoomDatabase;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
//upgrade the database version by increasing version value by one
@Database(entities = {ToDoTable.class},version = 2, exportSchema = false)
public abstract class MyRoomDatabase extends RoomDatabase {
public abstract MyDaoInterface myDaoInterface();
public static MyRoomDatabase mInstance;
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE to_Do_table "
+ " ADD COLUMN item_importance TEXT");
}
};
public static MyRoomDatabase getInstance(Context context){
if (mInstance == null){
mInstance = Room.databaseBuilder(context,MyRoomDatabase.class,"MyDatabaseName")
.addMigrations(MIGRATION_1_2)
//.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build();
}
return mInstance;
}
}
Finally run your app. If the data that previously added removed please comment.
This is about Room Database Migration in Android Studio. I hope you enjoyed. Please share this article to spread knowledge.
Thank You
Next