/ Miscellaneous

Automated Android Room ORM migrations generator with compile-time code generation

Automated Android Room ORM migrations generator with compile-time code generation

Roomigrant

Roomigrant is a helper library to automatically generate Android Room library migrations using compile-time code generation.

Add to your project

To get a Git project into your build:

Step 1. Add JitPack in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

Step 2. Add actual Roomigrant library and compiler (preferrably after Room dependencies):

dependencies {
    // room
    implementation 'android.arch.persistence.room:runtime:1.1.1'
    kapt 'android.arch.persistence.room:compiler:1.1.1'
    // roomigrant
    implementation 'com.github.MatrixDev.Roomigrant:RoomigrantLib:0.1.1'
    kapt 'com.github.MatrixDev.Roomigrant:RoomigrantCompiler:0.1.1'
}

More info can be found at https://jitpack.io/#MatrixDev/Roomigrant

How does it work?

Roomigrant uses scheme files generated by Room library and generates migrations base on difference between them. This means that Room schem generation must be enabled in build.gradle file:

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

After that actual database class should be annotated to enable migrations generator:

@Database(version = 5, entities = [Object1Dbo::class, Object2Dbo::class])
@GenerateRoomMigrations
abstract class Database : RoomDatabase() {
    // ...
}

And finally migrations can be added to actual Room database creation:

Room.databaseBuilder(appContext, Database::class.java, "database.sqlite")
		.addMigrations(*Database_Migrations.build())
		.build()

Default rules

Roominator will try its best to migrate everything by itself. It default rules are:

  • columns that have new affinity/type will use SQLite's CAST method
  • null cells became not nullable will take default value for thir's affinity/type
  • new columns will be initialized to default value
    -- 0 for INTEGER
    -- 0.0 for REAL
    -- "" for TEXT or BLOB

Because of SQLite limitations when any change other than adding new column is done:

  • new merge table will be created
  • data copied from original table to merge table
  • original table will be removed
  • merge table will be renamed back to original

Custom rules

Sometimes it will be required to write custom migration rules. It can be done by adding classes to GenerateRoomMigrations annotation:

@Database(version = 5, entities = [Object1Dbo::class, Object2Dbo::class])
@GenerateRoomMigrations(Rules::class)
abstract class Database : RoomDatabase() {
    // ...
}

And after that adding methods annotated with FieldMigrationRule annotation to provided classes:

// version 3 had Object1Dbo.intVal column
// version 4 has Object1Dbo.intValRenamed column
@FieldMigrationRule(version1 = 3, version2 = 4, table = "Object1Dbo", field = "intValRenamed")
fun migrate_3_4_Object1Dbo_intVal(): String {
	return "`Object1Dbo`.`intVal`"
}

Returned value will be injected as-is to final SQL statement when copying/updating that field.

Todos

  • Add table and column names escaping
  • Add foreign key support (currently they are completely ignored)
  • Add OnMigrationStart and OnMigrationEnd annotated callbacks
  • Some internal optimizations

GitHub