새 테이블 만 추가 된 경우 Room 데이터베이스 마이그레이션


99

간단한 Room 데이터베이스가 있다고 가정하겠습니다.

@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

이제 새 엔티티를 추가하고 Pet버전을 2로 변경합니다.

@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

물론 Room은 예외를 발생시킵니다. java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

User클래스를 변경하지 않았다고 가정하면 (모든 데이터가 안전하므로) 새 테이블을 만드는 마이그레이션을 제공해야합니다. 그래서 저는 Room에서 생성 된 클래스를 살펴보고 생성 된 쿼리를 검색하여 새 테이블을 만들고 복사 한 다음 마이그레이션에 붙여 넣습니다.

final Migration MIGRATION_1_2 =
        new Migration(1, 2) {
            @Override
            public void migrate(@NonNull final SupportSQLiteDatabase database) {
                database.execSQL("CREATE TABLE IF NOT EXISTS `Pet` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))");
            }
        };

그러나 수동으로 수행하는 것이 불편합니다. Room에 알리는 방법이 있습니까? 기존 테이블을 건드리지 않기 때문에 데이터가 안전합니다. 나를 위해 마이그레이션을 만드시겠습니까?


이에 대한 해결책을 찾았습니까?
Mikkel Larsen

3
나는 같은 문제가 있었고 당신이 한 것과 같은 방식으로 고쳤고 해결책을 찾지 못했습니다. 내가 혼자가 아니니 다행이다. :)
Mikkel Larsen

3
여기도 마찬가지입니다. 방이 database_impl 내부에서 생성 쿼리를 생성 할 수 있다는 점이 매우 불편하다는 것을 알게되었습니다. 그러나 마이그레이션이 시작되면 테이블을 생성 할 수 없습니다 ....
JacksOnF1re

1
나는 그러한 기능에 너무 많은 것을 줄 것입니다 ... 마이그레이션과 폴백 메커니즘을 혼합하는 것도 좋을 것입니다 ...
Appyx

3
이것이 도움이 될지 확실하지 않지만 Room에는 데이터베이스 스키마를 JSON 파일로 내보내는 옵션이 있습니다. developer.android.com/training/data-storage/room/… 분명히 이것은 마이그레이션 스크립트를 수동으로 추가하는 것을 의미하지만 SQL 문을 얻기 위해 자동 생성 된 클래스를 통해 라우팅 할 필요가 없습니다.
James Lendrem 2018

답변:


76

방은 않습니다 하지 적어도되지 때까지, 좋은 마이그레이션 시스템을 가지고 2.1.0-alpha03.

따라서 더 나은 마이그레이션 시스템을 갖출 때까지 회의실에서 쉽게 마이그레이션 할 수있는 몇 가지 해결 방법이 있습니다.

@Database(createNewTables = true) 또는 같은 방법이 없기 MigrationSystem.createTable(User::class)때문에 가능한 유일한 방법은 실행하는 것입니다.

CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))

당신의 migrate방법 내부 .

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))")
    }
}

위의 SQL 스크립트 를 얻으려면 4 가지 방법이 있습니다.

1. 혼자서 쓰기

기본적으로 Room에서 생성하는 스크립트와 일치하는 위의 스크립트를 작성해야합니다. 이 방법은 가능하지만 불가능합니다. (50 개의 필드가 있다고 가정)

2. 스키마 내보내기

주석 exportSchema = true안에 포함하면 @DatabaseRoom은 프로젝트 폴더의 / schemas 내에 데이터베이스 스키마를 생성합니다. 사용법은

@Database(entities = [User::class], version = 2, exportSchema = true)
abstract class AppDatabase : RoomDatabase {
   //...
}

build.grade앱 모듈의 아래 줄을 포함했는지 확인하십시오.

kapt {
    arguments {
        arg("room.schemaLocation", "$projectDir/schemas".toString())
    }
} 

프로젝트를 실행하거나 빌드하면 2.jsonRoom 데이터베이스 내에 모든 쿼리가 포함 된 JSON 파일 이 제공됩니다.

  "formatVersion": 1,
  "database": {
    "version": 2,
    "identityHash": "325bd539353db508c5248423a1c88c03",
    "entities": [
      {
        "tableName": "User",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": true
          },

따라서 위의 내용 createSqlmigrate메서드에 포함 할 수 있습니다 .

3. AppDatabase_Impl에서 쿼리 가져 오기

스키마를 내 보내지 않으려면 AppDatabase_Impl.java파일 을 생성 할 프로젝트를 실행하거나 빌드하여 쿼리를 가져올 수 있습니다 . 지정된 파일 내에서 가질 수 있습니다.

@Override
public void createAllTables(SupportSQLiteDatabase _db) {
  _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");

createAllTables메서드 내에 모든 엔터티의 생성 스크립트가 있습니다. 당신은 그것을 얻고 당신의 migrate방법에 포함시킬 수 있습니다 .

4. 주석 처리.

당신이 추측 할 수 있듯이, 방은 위에서 언급 한 모든를 생성 schema하고, AppDatabase_Impl당신이 추가 주석 처리 컴파일 시간 내에와의 파일

kapt "androidx.room:room-compiler:$room_version"

즉, 동일한 작업을 수행하고 필요한 모든 쿼리를 생성하는 고유 한 주석 처리 라이브러리를 만들 수 있습니다.

아이디어는 @Entity및의 Room 주석에 대한 주석 처리 라이브러리를 만드는 것입니다 @Database. @Entity예를 들어 주석이 달린 클래스를 살펴보십시오 . 따라야 할 단계입니다.

  1. 새로 StringBuilder만들고 "CREATE TABLE IF NOT EXISTS"를 추가합니다.
  2. 의 필드 에서 class.simplename또는 tableName필드 별로 테이블 이름을 가져옵니다 @Entity. 당신의StringBuilder
  3. 그런 다음 클래스의 각 필드에 대해 SQL 열을 만듭니다. 필드 자체 또는 @ColumnInfo주석으로 필드의 이름, 유형, 널 허용 여부를 가져옵니다 . 모든 필드 id INTEGER NOT NULL에 대해 열 스타일을 StringBuilder.
  4. 다음을 통해 기본 키 추가 @PrimaryKey
  5. 추가 ForeignKey하고 Indices존재하는 경우.
  6. 완료 후 문자열로 변환하고 사용하려는 새 클래스에 저장하십시오. 예를 들어 아래와 같이 저장합니다.
public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

그런 다음 다음과 같이 사용할 수 있습니다.

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL(UserSqlUtils().createTable)
    }
}

나는 당신이 체크 아웃 할 수 있고 당신의 프로젝트에서도 사용할 수있는 그런 라이브러리를 직접 만들었다. 내가 만든 라이브러리가 꽉 차지 않았고 테이블 생성에 대한 요구 사항 만 충족합니다.

더 나은 마이그레이션을위한 RoomExtension

RoomExtension을 사용하는 응용 프로그램

도움이 되었기를 바랍니다.

최신 정보

이 답변을 작성 2.1.0-alpha03했을 때 룸 버전은 였고 개발자에게 이메일을 보냈을 때 응답을 받았습니다.

더 나은 마이그레이션 시스템이있을 것으로 예상됩니다. 2.2.0

불행히도 우리는 여전히 더 나은 마이그레이션 시스템이 부족합니다.


3
Room 2.2.x가 더 나은 마이그레이션을 제공한다는 것을 어디에서 읽을 수 있습니까? 나는 그 주장을하는 어떤 것도 찾을 수없고, 우리는 현재 2.1.0 베타를 작업하고 있기 때문에 현재 2.2.0에있는 것은 알려지지 않은 것 같습니다.
jkane001

1
@ jkane001 룸 개발자 중 한 명에게 이메일을 보내 응답을 받았습니다. 2.2.x (아직?)에 대한 그러한 공고는 없습니다
musooff

4
현재 버전 2.2.2에 있고 여전히 더 나은 마이그레이션은 없습니다 :( 그러나 이것은 훌륭한 답변이며
그에

@androiddeveloper 주석 처리 인 # 4를 제외한 모두
musooff

1
@musooff 실제로 테이블 생성을 추가해도 괜찮다고 생각합니다. "createAllTables"함수에서 코드를 복사하는 가장 안전한 방법입니다.
안드로이드 개발자

3

죄송합니다. Room은 데이터 손실없이 테이블 자동 생성을 지원하지 않습니다.

마이그레이션을 작성하는 것은 필수입니다. 그렇지 않으면 모든 데이터가 지워지고 새 테이블 구조가 생성됩니다.


0

이렇게 할 수 있습니다.

@Database(entities = {User.class, Pet.class}, version = 2)

abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
public abstract Dao getPetDao();
}

나머지는 위에서 언급 한 것과 동일합니다.

 db = Room.databaseBuilder(this, AppDatabase::class.java, "your_db")
        .addMigrations(MIGRATION_1_2).build()

참조- 자세한 내용은


0

app.gradle의 defaultConfig에 다음 gradle 명령을 추가 할 수 있습니다.

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

이것을 실행하면 관련 CREATE TABLE 문을 사용하여 테이블 이름 목록을 컴파일하여 마이그레이션 개체에 복사하여 붙여 넣을 수 있습니다. 테이블 이름을 변경해야 할 수 있습니다.

예를 들어 이것은 내 생성 된 스키마에서 가져온 것입니다.

"tableName": "assets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` INTEGER NOT NULL, `type` INTEGER NOT NULL, `base` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`asset_id`))"

그래서 createSql 문을 복사하여 붙여넣고 '$ {TABLE_NAME}'을 테이블 이름 'assets'로 변경하고 자동 생성 된 Room create 문을 복사합니다.


-1

이 경우 마이그레이션을 수행 할 필요가 없습니다. 데이터베이스 인스턴스를 만들 때 .fallbackToDestructiveMigration ()을 호출 할 수 있습니다.

예:

    instance = Room.databaseBuilder(context, AppDatabase.class, "database name").fallbackToDestructiveMigration().build();

그리고 데이터베이스 버전을 변경하는 것을 잊지 마십시오.


이 솔루션은 기존 테이블에서 모든 데이터를 제거합니다.
Piotr Aleksander Chmielowski

불행히도 그렇습니다. "이 메서드를 호출하여 충돌하는 대신 데이터베이스를 다시 생성하도록이 동작을 변경할 수 있습니다. 이렇게하면 Room에서 관리하는 데이터베이스 테이블의 모든 데이터가 삭제됩니다."
rudicjovan

-2

아마도이 경우 (다른 테이블을 변경하지 않고 새 테이블 만 만든 경우) 마이그레이션을 전혀 만들지 않고 이렇게 할 수 있습니까?


1
아니요,이 경우 방에 로그가 발생합니다. java.lang.IllegalStateException : {old_version}에서 {new_version}으로 마이그레이션해야합니다.
Max Makeichik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.