티스토리 뷰

728x90



이번 포스팅에서는 SQLite 에서 Room 으로 migration 설정에 대해 알아보자

 

참고로, 지난 포스팅 내용은 하단과 같다.

- UI, DB 생성, CRUD, column 추가, DB 확인

[Android/Database] - SQLite 에서 ROOM 까지 (2) - UI 구성

[Android/Database] - SQLite 에서 ROOM 까지 (3) - SQLiteOpenHelper

[Android/Database] - SQLite 에서 ROOM 까지 (4) - SQLite 를 이용한 CRUD

[Android/Database] - SQLite 에서 ROOM 까지 (5) - SQLite 로 생성된 기존 DB 에 column 추가하기

[Android/Database] - SQLite 에서 ROOM 까지 (6) - DB 확인하기

- ROOM, ROOM 의 3구성요소 추가, Repository, ViewModel 추가

[Android/Database] - SQLite 에서 ROOM 까지 (7) - ROOM 이란?

[Android/Database] - SQLite 에서 ROOM 까지 (8) - ROOM 의 3 구성요소(Database, Entity, Dao) 추가하기

[Android/Database] - SQLite 에서 ROOM 까지 (9) - Architecture Components 구성을 위한 Repository, ViewModel 추가하기



Step 7: Migration 설정하기

◼ Version

SQLite 에서 Room 으로 migration 하기 위한 version 을 명시한다.

Room 은 SQLite 와 별도의 버전을 관리하지만 편의를 위해 SQLite 가 2 를 써서 3으로 설정했다.

(Room 부터는 새로운 버전으로 시작하고 싶다면 1 로 설정해도 된다.)

@Database(
entities = {PhStudentEntity.class},
version = 3
)
public abstract class PhDatabase extends RoomDatabase
view raw PhDatabase.java hosted with ❤ by GitHub

 

◼ Migration

해당 포스팅에서는 SQLite 에서 Room 으로 기존 data 를 유지한다는 조건으로 migration 을 하고 있다.

기존 data 를 유지하면서 Room 으로만 migration 한다면 아무것도 안 하고 addMigration 해주자.

(사실 아무것도 안 해줘도 한번에 성공하긴 힘들 것이다. 그 이유는 migration error 를 참조 하기 바란다.)

public abstract class PhDatabase extends RoomDatabase {
/**
* Migrate from:
* version 2 - using the SQLiteDatabase API
* to
* version 3 - using Room
*/
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase a_database) {
// Since we didn't alter the table, there's nothing else to do here.
};
public static PhDatabase getInstance(final Context a_context) {
if (INSTANCE == null) {
synchronized (PhDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(a_context.getApplicationContext(),
PhDatabase.class, PhDatabaseContract.DATABASE_NAME)
.addMigrations(MIGRATION_2_3)
.build();
}
}
}
return INSTANCE;
}
}
view raw PhDatabase.java hosted with ❤ by GitHub



Migration error

SQLite 에서 Room 으로 migration 하면서 가장 힘들었던 부분이다.

분명히 Google guide 에서 기존 data 를 유지하면서 아무것도 안 바꾸고 그냥 SQLite 에서 Room 으로 migration 하면 empty migration 만 추가해 주면 된다고 했다. 

그걸 믿고 진행했더니 아래와 같은 error 를 마주하게 됐다.

2020-09-09 09:27:12.160 15528-15600/com.parkho.sqlite E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: com.parkho.sqlite, PID: 15528
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$4.done(AsyncTask.java:399)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Caused by: java.lang.IllegalStateException: Migration didn't properly handle student(com.parkho.sqlite.database.PhStudentEntity).
Expected:
TableInfo, column_number=Column, _id=Column, column_age=Column, column_grade=Column}, foreignKeys=[], indices=[]}
Found:
TableInfo, column_grade=Column, column_name=Column, column_number=Column, _id=Column}, foreignKeys=[], indices=[]}
at com.parkho.sqlite.database.PhDatabase_Impl$1.validateMigration(PhDatabase_Impl.java:74)
at android.arch.persistence.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:87)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:133)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:417)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:317)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:96)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:54)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:233)
at com.parkho.sqlite.database.PhStudentDao_Impl.getAllStudents(PhStudentDao_Impl.java:118)
at com.parkho.sqlite.database.PhStudentRepository$getAllUsersAsyncTask.doInBackground(PhStudentRepository.java:59)
at com.parkho.sqlite.database.PhStudentRepository$getAllUsersAsyncTask.doInBackground(PhStudentRepository.java:50)
at android.os.AsyncTask$3.call(AsyncTask.java:378)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
... 4 more
view raw Log hosted with ❤ by GitHub



◼ IllegalStateException: Migration didn't properly handle student(com.parkho.sqlite.database.PhStudentEntity)

Exception 의 내용은 Room 에서 필요한 entity 와 migration 한 entity 가 다르다는 것이다.

 

Log 분석은 아래와 같이 Room SQLite Difference Finder 을 이용하자.

(그냥 눈으로 비교하기 힘들고 사용법도 간단히 copy & paste 만 하면 된다.)

 

기존 SQLite 프로젝트를 Room 으로 migration 하면 대부분 "notNull" 부분에서 오류가 날 것이다.

SQLite 에서는 "notNull" 항목을 신경쓰지 않았는데 (아마 default 가 false 였나보다.) Room 에서는 이게 틀리단다.

요것 때문에 empty migration 만 가지고는 대부분 에러가 날 것이다.

(참고로 다른 error 가 발생해도 RoomSQLiteDifferenceFinder 를 써서 틀린 부분을 똑같이 맞춰주면 해결된다.)

 

◼ Solution

가급적 empty migration 으로 진행하려 했지만 마땅한 해결책을 못 찾아 결국 migration 을 하단과 같이 수정했다.

1. 신규 table 생성

2. 기존 table 에서 신규 table 로 data copy

3. 기존 table 삭제

4. 신규 table 이름을 기존 table 이름으로 변경

 

public abstract class PhDatabase extends RoomDatabase {
/**
* Migrate from:
* version 2 - using the SQLiteDatabase API
* to
* version 3 - using Room
*/
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase a_database) {
// Create the new table
a_database.execSQL(
"CREATE TABLE student_new (" +
"_id INTEGER NOT NULL, " +
"column_grade INTEGER NOT NULL, " +
"column_number INTEGER NOT NULL, " +
"column_name TEXT, " +
"column_age INTEGER NOT NULL, " +
"PRIMARY KEY(_id))"
);
// Copy the data
a_database.execSQL(
"INSERT INTO student_new (_id, column_grade, column_number, column_name, column_age) " +
"SELECT _id, column_grade, column_number, column_name, column_age FROM student");
// Remove the old table
a_database.execSQL("DROP TABLE student");
// Change the table name to the correct one
a_database.execSQL("ALTER TABLE student_new RENAME TO student");
}
};
}
view raw PhDatabase.java hosted with ❤ by GitHub



Source code

https://github.com/parkho79/FromSQLiteToRoom_3



728x90
댓글