Migrasi database ruangan jika hanya tabel baru yang ditambahkan

107

Jangan berasumsi, saya memiliki database Room sederhana:

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

Sekarang, saya menambahkan entitas baru: Petdan mengganti versi ke 2:

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

Tentu saja, Room memberikan pengecualian: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

Dengan asumsi, saya belum mengubah Userkelas (jadi semua data aman), saya harus menyediakan migrasi yang hanya membuat tabel baru. Jadi, saya mencari kelas yang dihasilkan oleh Room, mencari kueri yang dihasilkan untuk membuat tabel baru saya, menyalinnya dan menempelkannya ke migrasi:

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`))");
            }
        };

Namun saya merasa tidak nyaman untuk melakukannya secara manual. Adakah cara untuk memberi tahu Room: Saya tidak menyentuh salah satu tabel yang ada, jadi data aman. Tolong buat migrasi untuk saya?

Piotr Aleksander Chmielowski
sumber
Apakah Anda menemukan solusi untuk ini?
Mikkel Larsen
3
Saya memiliki masalah yang sama dan memperbaikinya dengan cara yang sama seperti yang Anda lakukan, dan juga belum menemukan solusi. Senang aku tidak sendiri saat itu. :)
Mikkel Larsen
3
Sama disini. Saya merasa sangat tidak nyaman karena ruangan dapat menghasilkan kueri pembuatan di dalam database_impl, tetapi tidak bisa begitu saja membuat tabel, setelah migrasi dimulai ....
JacksOnF1re
1
Saya akan memberikan begitu banyak untuk fitur seperti itu ... Akan menyenangkan juga untuk mencampur migrasi dan mekanisme fallback ...
Appyx
3
Saya tidak yakin apakah ini akan membantu tetapi Room memang memiliki opsi untuk mengekspor skema database ke file JSON. developer.android.com/training/data-storage/room/… Jelas ini masih berarti menambahkan skrip migrasi secara manual tetapi Anda tidak perlu merutekan melalui kelas yang dibuat secara otomatis untuk mendapatkan pernyataan SQL Anda.
James Lendrem

Jawaban:

84

Ruangan yang tidak memiliki baik Migrasi Sistem, setidaknya tidak sampai 2.1.0-alpha03.

Jadi, hingga kami memiliki Sistem Migrasi yang lebih baik, ada beberapa solusi untuk memudahkan Migrasi di dalam Ruangan.

Karena tidak ada metode seperti @Database(createNewTables = true) atau MigrationSystem.createTable(User::class), yang seharusnya ada satu atau lainnya, satu-satunya cara yang mungkin adalah berjalan

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

di dalam migratemetode Anda .

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`))")
    }
}

Untuk mendapatkan skrip SQL di atas , Anda memiliki 4 cara

1. Tulis sendiri

Pada dasarnya, Anda harus menulis skrip di atas yang akan cocok dengan skrip yang dihasilkan Room. Cara ini mungkin, tidak mungkin. (Pertimbangkan Anda memiliki 50 bidang)

2. Skema Ekspor

Jika Anda menyertakan exportSchema = truedi dalam @Databaseanotasi Anda , Room akan menghasilkan skema database dalam / skema folder proyek Anda. Penggunaannya

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

Pastikan Anda telah menyertakan baris di bawah ini dalam build.grademodul aplikasi Anda

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

Saat Anda menjalankan atau membangun proyek, Anda akan mendapatkan file JSON 2.json, yang memiliki semua kueri dalam database Room Anda.

  "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
          },

Jadi, Anda dapat memasukkan di atas createSqldalam migratemetode Anda .

3. Dapatkan kueri dari AppDatabase_Impl

Jika Anda tidak ingin mengekspor skema, Anda masih bisa mendapatkan kueri dengan menjalankan atau membangun proyek yang akan menghasilkan AppDatabase_Impl.javafile. dan dalam file tertentu yang dapat Anda miliki.

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

Dalam createAllTablesmetode ini, akan ada skrip buat semua entitas. Anda bisa mendapatkannya dan memasukkannya ke dalam migratemetode Anda .

4. Pemrosesan Anotasi.

Seperti yang Anda duga, Room menghasilkan semua yang disebutkan di atas schema, dan AppDatabase_Implfile dalam waktu kompilasi dan dengan Pemrosesan Anotasi yang Anda tambahkan

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

Itu berarti Anda juga dapat melakukan hal yang sama dan membuat pustaka pemrosesan anotasi Anda sendiri yang menghasilkan semua kueri pembuatan yang diperlukan untuk Anda.

Idenya adalah membuat pustaka pemrosesan anotasi untuk anotasi Room @Entitydan @Database. Ambil kelas yang diberi catatan @Entitymisalnya. Ini adalah langkah-langkah yang harus Anda ikuti

  1. Buat yang baru StringBuilderdan tambahkan "BUAT TABEL JIKA TIDAK ADA"
  2. Dapatkan nama tabel baik dari class.simplenameatau menurut tableNamebidang @Entity. Tambahkan keStringBuilder
  3. Kemudian untuk setiap bidang kelas Anda buat kolom SQL. Ambil nama, jenis, nullability bidang baik menurut bidang itu sendiri atau dengan @ColumnInfoanotasi. Untuk setiap bidang, Anda harus menambahkan id INTEGER NOT NULLgaya kolom ke Anda StringBuilder.
  4. Tambahkan kunci utama dengan @PrimaryKey
  5. Tambahkan ForeignKeydan Indicesjika ada.
  6. Setelah selesai mengubahnya menjadi string dan menyimpannya di beberapa kelas baru yang ingin Anda gunakan. Misalnya simpan seperti di bawah ini
public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

Kemudian, Anda dapat menggunakannya sebagai file

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

Saya membuat perpustakaan seperti itu untuk diri saya sendiri yang dapat Anda periksa, dan bahkan menggunakannya dalam proyek Anda. Perhatikan bahwa perpustakaan yang saya buat tidak penuh dan hanya memenuhi persyaratan saya untuk pembuatan tabel.

RoomExtension untuk Migrasi yang lebih baik

Aplikasi yang menggunakan RoomExtension

Semoga bermanfaat.

MEMPERBARUI

Pada saat menulis jawaban ini, versi kamar adalah 2.1.0-alpha03dan ketika saya mengirim email kepada pengembang, saya mendapat tanggapan

Diharapkan memiliki Sistem Migrasi yang lebih baik 2.2.0

Sayangnya, kami masih kekurangan Sistem Migrasi yang lebih baik.

musooff
sumber
3
Bisakah Anda menunjuk ke tempat Anda membaca bahwa Room 2.2.x akan memiliki migrasi yang lebih baik? Saya tidak dapat menemukan apa pun yang membuat klaim tersebut, dan karena saat ini kami sedang mengerjakan 2.1.0 beta, apa yang ada di 2.2.0 tampaknya tidak diketahui saat ini.
jkane001
1
@ jkane001 Saya mengirim email ke salah satu pengembang ruang dan mendapat tanggapan begitu. Tidak ada pemberitahuan publik tentang 2.2.x (belum?)
musooff
5
Saat ini menggunakan versi 2.2.2 dan masih belum ada migrasi yang lebih baik :( Namun, ini adalah jawaban yang sangat baik dan menghemat banyak pekerjaan, jadi +1 untuk itu.
smitty1
@androiddeveloper Semua kecuali # 4, yang Pemrosesan Anotasi
musooff
1
@musooff Saya sebenarnya berpikir tidak apa-apa menambahkan pembuatan tabel. Ini adalah cara teraman untuk menyalin kode dari fungsi "createAllTables".
Pengembang android
5

Maaf, Room tidak mendukung pembuatan tabel secara otomatis tanpa kehilangan data.

Migrasi wajib ditulis. Jika tidak, ini akan menghapus semua data dan membuat struktur tabel baru.

Viswanath Kumar Sandu
sumber
0

Anda bisa melakukan ini-

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

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

Sisa akan sama seperti yang Anda sebutkan di atas-

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

Referensi - Untuk lebih lanjut

Sujeet Kumar
sumber
0

Anda dapat menambahkan perintah gradle berikut ke defaultConfig Anda di app.gradle:

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

Ketika Anda menjalankan ini, itu akan menyusun daftar nama tabel dengan pernyataan CREATE TABLE yang relevan dari mana Anda bisa menyalin dan menempelkannya ke objek migrasi Anda. Anda mungkin harus mengubah nama tabel.

Misalnya ini dari skema yang saya buat:

"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`))"

Jadi saya menyalin dan menempelkan pernyataan createSql dan mengubah '$ {TABLE_NAME}' menjadi 'aset' nama tabel, dan pernyataan Room membuat dibuat secara otomatis.

Larry Stent
sumber
-1

Dalam kasus ini, Anda tidak perlu melakukan migrasi, Anda dapat memanggil .fallbackToDestructiveMigration () saat Anda membuat instance database.

Contoh:

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

Dan jangan lupa untuk mengubah versi database.

rudicjovan.dll
sumber
Solusi ini akan menghapus semua data saya dari tabel yang ada.
Piotr Aleksander Chmielowski
Sayangnya ya. "Anda dapat memanggil metode ini untuk mengubah perilaku ini untuk membuat ulang database alih-alih mogok. Perhatikan bahwa ini akan menghapus semua data dalam tabel database yang dikelola oleh Room."
rudicjovan
-2

Mungkin dalam kasus ini (jika Anda hanya membuat tabel baru tanpa mengubah yang lain) Anda dapat melakukan ini tanpa membuat migrasi sama sekali?

pengguna1730694
sumber
1
Tidak, dalam hal ini room memasukkan log: java.lang.IllegalStateException: Migrasi dari {old_version} ke {new_version} diperlukan
Max Makeichik