티스토리 뷰
SQLite 에서 ROOM 까지 (10) - SQLite 에서 ROOM 으로 migration 설정하기
parkho79 2020. 9. 21. 09:04이번 포스팅에서는 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) 추가하기
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 |
◼ 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; | |
} | |
} |
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 |
◼ 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"); | |
} | |
}; | |
} |
Source code
https://github.com/parkho79/FromSQLiteToRoom_3
'Android > Database' 카테고리의 다른 글
SQLite 에서 ROOM 까지 (12) - ROOM 에 LiveData 적용하기 (0) | 2020.10.08 |
---|---|
SQLite 에서 ROOM 까지 (11) - ROOM 을 이용한 CRUD (0) | 2020.10.05 |
SQLite 에서 ROOM 까지 (9) - Architecture Components 구성을 위한 Repository, ViewModel 추가하기 (0) | 2020.09.15 |
SQLite 에서 ROOM 까지 (8) - ROOM 의 3 구성요소(Database, Entity, Dao) 추가하기 (0) | 2020.09.09 |
SQLite 에서 ROOM 까지 (7) - ROOM 이란? (0) | 2020.09.01 |
- Total
- Today
- Yesterday
- onCreateContextMenu
- bindservice
- notifyDataSetChanged
- StringUtils
- 안드로이드 서비스
- android activity flag
- onContextItemSelected
- 안드로이드 인텐트
- task 생성
- 채굴앱
- registerForContextMenu
- android flag activity
- Android Service
- 리워드 어플
- 앱테크
- 무료채굴
- android task
- mPANDO
- p2e
- Intent
- 무료 채굴
- 리워드앱
- BroadcastReceiver
- 앱테크 추천
- task
- RoomDatabase
- M2E
- WEMIX
- android intent
- StartService
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |