Employing Migrations

Simply creating a Migration as an object somewhere is necessary but not sufficient to have Room know about performing the migration. Instead, you need to use the addMigrations() method on RoomDatabase.Builder to teach Room about your Migration objects. addMigrations() accepts a varargs, and so you can pass in one or several Migration objects as needed.

package com.commonsware.room.migration

import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase

@Database(entities = [NoteEntity::class], version = 2)
abstract class NoteDatabase : RoomDatabase() {
  companion object {
    fun newTestDatabase(context: Context) = Room.inMemoryDatabaseBuilder(
      context,
      NoteDatabase::class.java
    )
      .addMigrations(MIGRATION_1_2)
      .build()
  }

  abstract fun notes(): NoteStore
}

@VisibleForTesting
internal val MIGRATION_1_2 = object : Migration(1, 2) {
  override fun migrate(db: SupportSQLiteDatabase) {
    db.execSQL("ALTER TABLE notes ADD COLUMN andNowForSomethingCompletelyDifferent TEXT")
  }
}

This version of NoteDatabase has a companion object with a newTestDatabase() function that our tests can use. As part of building the NoteDatabase instance, we use addMigrations(MIGRATION_1_2) to teach Room about our Migration.

Also, note that the version in the @Database annotation is 2. In theory, this module demonstrates an app that has been modified from its original. The code started with the NoteBasics edition of NoteEntity:

package com.commonsware.room.notes

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "notes")
data class NoteEntity(
  @PrimaryKey val id: String,
  val title: String,
  val text: String,
  val version: Int
)

The Migration module added a new nullable property to NoteEntity:

package com.commonsware.room.migration

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "notes")
data class NoteEntity(
  @PrimaryKey val id: String,
  val title: String,
  val text: String,
  val version: Int,
  val andNowForSomethingCompletelyDifferent: String?
)

That property is the column that we are adding in MIGRATION_1_2, to handle a user that had been using our app with schema version 1 and now upgrades to a newer copy of our app that uses schema version 2.

In principle, MIGRATION_1_2 could be private to NoteDatabase. However, we want to be able to test our migration, so we have it marked as internal instead, with the @VisibleForTesting annotation to help discourage unexpected use.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.