Sling Academy
Home/Kotlin/Using Room Database Migrations in Kotlin

Using Room Database Migrations in Kotlin

Last updated: November 30, 2024

In modern Android development, it's crucial to handle data storage efficiently, especially when your application evolves and the data schema changes. Room, a part of the Android Architecture Components, provides an abstraction layer over SQLite to facilitate database access. One of the significant features of Room is its support for database migrations, allowing you to change the schema without losing the users' data.

Understanding Room Migrations

When you modify the database schema, for example, by adding a new column or table, you must provide a migration path from the old to the new schema. Failing to do so results in a runtime crash. Migrations in Room are handled by the Migration class, where you define how to transform data from the old version to the new version.

Defining Your Entities

Let's start by defining a simple Entity in your Kotlin project for a User:


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

@Entity
data class User(
    @PrimaryKey val uid: Int,
    val firstName: String,
    val lastName: String)

Setting Up Room Database

In Room, you need to create an abstract class extending RoomDatabase and annotate it with @Database.


import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

Creating a Migration

Suppose we want to add a new column named email to the User table. This change requires a migration from version 1 to version 2.


val migration_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE User ADD COLUMN email TEXT")
    }
}

Integrating the Migration

You register the migration in your RoomDatabase.Builder:


import android.content.Context
import androidx.room.Room

fun getDatabase(context: Context): AppDatabase {
    return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "database-name")
        .addMigrations(migration_1_2)
        .build()
}

Testing Migrations

It is important to test your migrations thoroughly. Room provides testing tools that facilitate running migration tests easily. Use the Room.inMemoryDatabaseBuilder and pass a list of your migrations:


import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory

@RunWith(AndroidJUnit4::class)
class MigrationTest {

    private val TEST_DB = "migration-test"

    @get:Rule
    val helper: MigrationTestHelper = MigrationTestHelper(
        InstrumentationRegistry.getInstrumentation(),
        AppDatabase::class.java.canonicalName,
        FrameworkSQLiteOpenHelperFactory())

    @Test
    fun migrate1To2() {
        var db = helper.createDatabase(TEST_DB, 1)
        // Perform schema alterations on db or insert test data
        db.close()
        db = helper.runMigrationsAndValidate(TEST_DB, 2, true, migration_1_2)
        // Confirm migration did the changes correctly
    }
}

By following these steps, you can effectively manage Room database migrations in your Kotlin applications, ensuring your data's integrity as your app matures.

Next Article: Kotlin: How to Handle Relationships in Room (One-to-Many, Many-to-Many)

Previous Article: Kotlin: Working with LiveData and Room for Real-Time Updates

Series: Kotlin - Interacting with Databases

Kotlin

You May Also Like

  • How to Use Modulo for Cyclic Arithmetic in Kotlin
  • Kotlin: Infinite Loop Detected in Code
  • Fixing Kotlin Error: Index Out of Bounds in List Access
  • Setting Up JDBC in a Kotlin Application
  • Creating a File Explorer App with Kotlin
  • How to Work with APIs in Kotlin
  • What is the `when` Expression in Kotlin?
  • Writing a Script to Rename Multiple Files Programmatically in Kotlin
  • Using Safe Calls (`?.`) to Avoid NullPointerExceptions in Kotlin
  • Chaining Safe Calls for Complex Operations in Kotlin
  • Using the Elvis Operator for Default Values in Kotlin
  • Combining Safe Calls and the Elvis Operator in Kotlin
  • When to Avoid the Null Assertion Operator (`!!`) in Kotlin
  • How to Check for Null Values with `if` Statements in Kotlin
  • Using `let` with Nullable Variables for Scoped Operations in Kotlin
  • Kotlin: How to Handle Nulls in Function Parameters
  • Returning Nullable Values from Functions in Kotlin
  • Safely Accessing Properties of Nullable Objects in Kotlin
  • How to Use `is` for Nullable Type Checking in Kotlin