[안드로이드] Room 데이터베이스 이전하기

동현·2020년 12월 30일
0
post-thumbnail
post-custom-banner

최근 Room 라이브러리를 이용해 비밀번호 저장 어플을 만들고 있는데 한 가지 문제점이 생겼었다. Room을 이용할 경우 데이터베이스의 스키마를 바꿀 때마다, version을 올려주게 되는데 만약 version을 2로 올렸을 때, version 1을 사용하고 있는 사용자의 데이터를 어떻게 이동시키냐가 문제였다. 서치를 해 본 결과, 그 방법은 안드로이드 공식 문서에서 찾아볼 수 있었다.

예시로 들 데이터베이스는 다음과 같다.

@Database(entities = {Site.class}, version = 2, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    public abstract SiteDao siteDao();

    private static volatile AppDatabase instance;

    public static AppDatabase getInstance(final Context context) {
        if (instance == null) {
            synchronized (AppDatabase.class) {
                instance = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app-db").build();
            }
        }
        return instance;
    }
}

버전 1에서의 Sites 테이블의 구조는 다음과 같다
eid(int) title(TEXT) id(TEXT) password(TEXT) url(TEXT)
버전 2에서의 Sites 테이블의 구조는 다음과 같다
id(int) title(TEXT) uid(TEXT) password(TEXT) url(TEXT)

열의 이름만 바꿔준 상태이고 이를 그대로 옮겨줄 것이다.

1. Migration 클래스 만들기

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE Sites RENAME COLUMN id to uid");
            database.execSQL("ALTER TABLE Sites RENAME COLUMN eid to id");
        }
    };

버전 1에서 2로 올려준 것이므로 매개 변수에 1, 2를 넣고 이전시 어떤 작업을 수행할지 Migration 클래스를 작성하면 된다.

Migration 클래스를 작성할 때 본래 테이블의 구조가 헷갈릴 때가 있는데, 이는 데이터베이스 스키마 정보를 내보냄으로써 테이블의 구조를 더 쉽게 확인할 수 있다.
스키마 정보를 내보내는 방법은 다음과 같다.

  1. @Database의 exportSchema를 true로 바꿔준다.
  2. 앱 수준의 build.gradle파일의 build.config 안에 다음 내용을 추가한다.
javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }

실행 시 app/schemas 폴더에 스키마가 json 형태로 내보내지는 것을 확인할 수 있다.

2. databaseBuilder 메소드에 추가

@Database(entities = {Site.class, Card.class}, version = 3, exportSchema = true)
public abstract class AppDatabase extends RoomDatabase {
    public abstract SiteDao siteDao();
    public abstract CardDao cardDao();

    private static volatile AppDatabase instance;

    public static AppDatabase getInstance(final Context context) {
        if (instance == null) {
            synchronized (AppDatabase.class) {
                instance = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app-db")
                        .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
            }
        }
        return instance;
    }
}

addMigrations에 만들어준 Migration 클래스를 넣어줌으로써 실행 시 데이터베이스 버전이 1이면 자동으로 Migration 클래스에 써놓은 대로 이전 작업을 실행한다.

+ Room 2.2.0 이상으로 버전을 업그레이드 했을 때

Room 2.2.0 아래의 버전에서 Room 2.2.0 이상으로 업그레이드했을 때는 다음과 같은 DROP & CREATE TABLE 전략이 권장된다.

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL(
                    "CREATE TABLE IF NOT EXISTS new_Sites (" +
                            "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                            "title TEXT, " +
                            "uid TEXT, " +
                            "password TEXT, " +
                            "url TEXT)"
            );
            database.execSQL("INSERT INTO new_Sites (id, title, uid, password, url) " +
                    "SELECT eid, title, id, password, url FROM site");
            database.execSQL("DROP TABLE Sites");
            database.execSQL("ALTER TABLE new_Sites RENAME TO Sites");
        }
    };

자세한 내용은 공식 문서의 Room 2.2.0으로 업그레이드 시 열 기본값 처리 섹션을 읽어보자.

3. 참조

Android developers, "Room 데이터베이스 이전", https://developer.android.com/training/data-storage/room/migrating-db-versions?hl=ko

profile
https://github.com/DongChyeon
post-custom-banner

0개의 댓글