프로그래밍 놀이터/안드로이드, Java

[android] Migrating Room databases - Room 에 대해 알아보자

돼지왕 왕돼지 2021. 5. 3. 12:51
반응형

 

-

Room 은 Migration class 를 써서 db 변화가 있을 때 migrate 를 할 수 있게 해준다.

Migration class 는 startVersion 과 endVersion 을 명시한다.

runtime 에 Room 은 각각의 Migration class 의 migrate() 함수를 호출해준다.

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(db: SupportSQLiteDatabase){
        db.execSQL("CREATE TABLE 'Fruit' ('id' INTEGER, 'name' TEXT, PRIMARY KEY('id'))")
    }
}

val MIGRATION_2_3 = object : Migration(2, 3){
    override fun migrate(db: SupportSQLiteDatabase){
        db.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")
    }
}

Room.databaseBuilder(appContext, MyDb::class.java, "db_name")
    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
    .build()

 

 

-

migration logic 이 제대로 동작하게 하려면, constants 를 ref 하는 것이 아닌 full query string 으로 가져가는 것이 좋다.

 

 

-

migration 이 끝난 후, Room 은 schema 를 검증해서 migration 이 제대로 되었는지를 확인한다.

Room 이 문제를 발견한다면 exception 을 던진다.

 

 

 

Test migrations

 

-

migration 내용은 쓰는 것이 쉽지 않고, crash 를 내기 쉽다.

앱의 안정성을 위해서 migration 에 대해 직접 test 를 해야 한다.

Room 은 test 를 위해 testing Maven artifact 를 제공한다.

하지만 이를 위해서는 db schema export 가 필요하다.

 

 

 

Export schemas

 

-

컴파일 후에 Room 은 db schema 정보를 JSON file 로 export 한다.

schema 를 export 하려면, build.gradle 의 annotationProcessorOption 으로 room.schemaLocation 값을 설정해줘야 한다.

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

 

 

-

이 exported 된 schema 를 version control system 에 포함하는 것이 좋다.

 

 

-

migration 을 test 하려면, android.arch.persistence.room:testing Maven artifact 를 dependency 로 추가하고, schema location 을 asset folder 로 두는 것이 좋다.

android {
    ...
    sourceSets {
        androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
    }
}

 

 

-

testing pkg 는 MigrationTestHelper class 를 제공하는데, 이 녀석이 schema file 을 읽을 수 있다.

그리고 이 녀석은 JUnit4 의 TestRule interface 를 구현하여, db 생성을 관리할 수 있다.

@RunWith(AndroidJUnit4::class)
class MigrationTest {
    private val TEST_DB = "migration-test"

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

    @Test
    @Throws(IOException::class)
    fun migrate1To2() {
        var db = helper.createDatabase(TEST_DB, 1).apply {
            // db has schema version 1. insert some data using SQL queries.
            // You cannot use DAO classes because they expect the latest schema.
            execSQL(...)

            // Prepare for the next version.
            close()
        }

        // Re-open the database with version 2 and provide
        // MIGRATION_1_2 as the migration process.
        db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)

        // MigrationTestHelper automatically verifies the schema changes,
        // but you need to validate that the data was migrated properly.
    }
}

 

 

 

Gracefully handle missing migration paths

 

-

db schema update 후, 몇몇 단말에서는 db 를 아직 오래된 schema 형태로 사용할 수도 있다.

Room 이 단말 db 의 upgrading migration 을 찾지 못한다면, IllegalStateException 이 발생한다.

이런 crash 가 발생하지 않게 하려면 fallbackToDestructiveMigration() 을 builder 에 호출해야 한다.

Room.databaseBuilder(appContext, MyDb::class.java, "db-name")
    .fallbackToDestructiveMigration()
    .build()

이 함수를 호출함으로서, Room 은 migration schema version 이 유실되어 migration 이 실패하는 경우 app db table 을 재생성한다.(destructive recreation) 이 때 영구적으로 모든 데이터가 유실될 수 있음을 명심해야 한다.

 

 

-

destructive recreation fallback logic 은 다음 추가적인 option 을 가진다.

fallbackToDestructiveMigrationFrom() 은 특정 버전의 schema 에서 에러가 발생해서 에러를 해결할 수 없을 때 쓰면 된다. 

fallbackToDestructiveMigrationOnDowngrade() 는 schema downgrade 를 하려고 할 때 발생할 때 쓰면 된다.

 

 

-

참고자료

https://developer.android.com/training/data-storage/room/migrating-db-versions

 

 

 

반응형