[JAVA] Wenn Sie Android Room verwenden und die Spaltendefinition ändern möchten

Raummigration ist nicht erlaubt

Ich entwickle eine Android-App mit "Room", einer von Google erstellten Persistenzbibliothek. Da es sich um eine relativ neue Bibliothek handelt, ist die Migrationsfunktion nicht cool. .. .. Was ich tun wollte, war "eine REAL-Typspalte in einen INTEGER-Typ ändern", aber die SQLite-Spezifikation, dass "nur das Hinzufügen von Spalten mit der ALTER-Anweisung möglich ist" und eine exquisite Ehe wurden erstellt und realisiert. Es fiel mir schwer, es zu tun ... Dieser Artikel ist dieses Memo.

Lösungsverfahren

Ich werde zuerst aus dem Lösungsverfahren schreiben.

  1. Ändern Sie den Eigenschaftstyp der Entität (in meinem Fall von Double? In Integer?)
  2. Entspricht dem durch 1 verursachten Erstellungsfehler.
  3. Erhöhen Sie die Version von @ Database
  4. Schreiben Sie den Migrationscode
    1. CREATE TABLE
    2. INSERT INTO ... SELECT
    3. DROP TABLE
    4. ALTER TABLE ... RENAME TO ...
  5. CREATE INDEX (wenn es einen Index gibt)

Ich glaube nicht, dass Sie es bei dieser Geschwindigkeit sehr gut verstehen können, also schauen wir uns jedes Detail an.

1. Ändern Sie den Eigenschaftstyp einer Entität

Zum Beispiel, wenn Sie die folgende Entität "Produkt" haben

Product.kt


@Entity(indices = [Index(value = ["name"])])
data class Product(
    @PrimaryKey(autoGenerate = true) var id: Int = 0,
    var name: String? = null,
    var price: Double? = null,
    ...

Ändern Sie das "Double" dieses "Preises" in "Int" und so weiter. Nun, das ist natürlich.

2. Reagieren Sie auf Buildfehler

Die Änderung von 1. sollte (normalerweise) einen Build-Fehler verursachen, also zerstören Sie ihn nacheinander. Wie das Problem behoben werden kann, hängt von der Implementierung ab. Daher werde ich es hier weglassen.

3. Inkrementieren Sie die Version von @ Database

Vielleicht definieren Sie eine sub-abstrakte Klasse für "RoomDatabase" wie folgt:

AppDatabase.kt


@Database(entities = [Product::class], version = 1)
abstract class AppDatabase : RoomDatabase() {

    companion object {

        @Volatile private var instance: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {
            return instance ?: synchronized(this) {
                instance ?: buildDatabase(context).also { instance = it }
            }
        }

        private fun buildDatabase(context: Context): AppDatabase {
            return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "hogehoge-database")
                .build()
        }
    }
}

Erhöhen Sie die Version hier um eins. In diesem Beispiel ist es auf 2 gesetzt. Das bedeutet, dass die Schemaversion der Datenbank auf 2 gesetzt wird.

4. Schreiben Sie den Migrationscode

Dies war der nervigste Geruch. Raum tut fast nichts gegen Migration. Daher müssen Sie unformatiertes SQL schreiben. Da die Angabe von SQLite lautet "Spalten können nicht geändert oder gelöscht werden", besteht die Methode darin, eine neue Tabelle zu definieren, die aktuellen Daten in diese zu übertragen, die alte Tabelle zu löschen und die neue Tabelle dann in ihren ursprünglichen Namen umzubenennen. Ich werde das machen. Schließlich ist dies eine Gefahr, aber der Index, der beim Löschen für die alte Tabelle festgelegt wurde, wird ebenfalls gelöscht. Sie müssen dies also nach dem Erstellen der neuen Tabelle erstellen.

Zunächst die Anweisung CREATE TABLE, aber ich denke, es ist sehr mühsam, von Grund auf neu zu schreiben. Verwenden wir also die Raumschemaausgabe json. Wenn die folgende Beschreibung in app / build.gradle enthalten ist, werden die Schemainformationen json beim Erstellen ausgespuckt.

app/build.gradle


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

Wenn Sie damit erstellen, werden die Schemainformationen unter app / schemas als json ausgegeben. Der Dateiname lautet * Schemaversion * .json.

Wenn ich mir diesen JSON anschaue, denke ich, dass die Anweisung CREATE TABLE im Schlüssel createSql definiert ist. Es ist einfach, es zu kopieren und die erforderlichen Änderungen vorzunehmen.

Der tatsächliche Migrationscode lautet also wie folgt. Nehmen Sie das vorherige Beispiel von "AppDatabase.kt"

AppDatabase.kt


...
        private fun buildDatabase(context: Context): AppDatabase {
            val migration1to2 = object : Migration(1, 2) {
                override fun migrate(database: SupportSQLiteDatabase) {
                    database.execSQL("CREATE TABLE IF NOT EXISTS `Geeigneter Tabellenname` ...")
                }
            }

            return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "onieasy-database")
                .addMigrations(migration1to2)
                .build()
        }
...

Ersetzen Sie die Schemaversion durch die tatsächliche.

Es wird später umbenannt, sodass Sie den "entsprechenden Tabellennamen" verwenden können. Neben TABELLE ERSTELLEN

nicht wahr.

            val migration1to2 = object : Migration(1, 2) {
                override fun migrate(database: SupportSQLiteDatabase) {
                    database.execSQL("TABELLE ERSTELLEN, WENN NICHT EXISTIERT Geeigneter Tabellenname...")
                    database.execSQL("INSERT INTO Geeigneter Tabellenname SELECT*FROM Tabellenname, den Sie ändern möchten")
                    database.execSQL("DROP TABLE Der Name der Tabelle, die Sie ändern möchten")
                    database.execSQL("ALTER TABLE Geeigneter Tabellenname RENAME TO Tabellenname, den Sie ändern möchten")
                }
            }

Wenn es ursprünglich indiziert wurde, kopieren Sie es schließlich aus dem Schema json. Es sollte "createSql" in "Indizes" geben.

                    database.execSQL("CREATE INDEX index_Product_name` ON `Product` (`name`)")

Es ist wie es ist.

Lassen Sie uns alles auf einmal tun, es erstellen und die App ausführen. Wenn Sie es richtig ändern können, sollte es normal funktionieren.

Warum ist es so kompliziert?

Der Raum entwickelt sich noch, daher denke ich, dass es eine Menge zu tun gibt. Die erste Idee, die ich hatte, war, eine neue Entität in Kotlin zu definieren und die Daten an diese neue Entität zu übertragen. Ich habe es versucht, aber das Ergebnis ist, dass die App nicht gestartet wird ... Wie, ** Room erstellt eine Tabelle aus einer Entität nur im Fall von Schema Version 1 (dh beim ersten Mal), aber danach wird die Tabelle generiert. Es wird nicht automatisch generiert! ** Was zur Hölle ...

Deshalb ist es bei der Verwendung von Room problematisch, wenn Sie den Tisch nicht von Anfang an genau entwerfen, ja, das habe ich gelernt.

Recommended Posts

Wenn Sie Android Room verwenden und die Spaltendefinition ändern möchten
Wenn Sie die Java-Entwicklungsumgebung von Eclipse aus ändern möchten
Wenn Sie die übergeordnete Klasse in Lomboks @builder aufnehmen möchten
Wenn Sie Rails noch nicht kennen und Ihre eigene Validierung vornehmen möchten, halten Sie an diesem Finger an.
Wenn Sie eine Methode in RSpec verspotten möchten, sollten Sie die Methode allow für mock und die Singleton-Methode verwenden.
[Android] So aktivieren / deaktivieren Sie das Benachrichtigungsfeld mit StatusBarManager
[Schienen] So erstellen Sie eine Tabelle, fügen eine Spalte hinzu und ändern den Spaltentyp
Wenn Sie die Testabdeckung privater Methoden in JUnit erfüllen möchten
Wenn Sie Datenbankspalten usw. ändern möchten.
Wenn Sie die Methode außerhalb verwenden möchten
Führen Sie Docker in die Anwendung ein, die Sie erstellen
[Java] Was tun, wenn sich der in der Datenbank gespeicherte Inhalt und der Name der Aufzählung in der Aufzählung unterscheiden, die die Definition der Datenbank widerspiegelt?
Wenn Sie die Spring Boot + Thymeleaf-Verarbeitung trennen möchten
[Ruby on Rails] So ändern Sie den Spaltennamen
Ändern Sie den Primärschlüssel der Datenbank (MySQL) in eine beliebige Spalte.
Ich möchte den Bereich anhand des monatlichen Abschlusses beurteilen
[Rails] So ändern Sie den Spaltennamen der Tabelle
Wenn Sie Mockito mit Kotlin verwenden möchten, verwenden Sie Mockito-Kotlin
Ich möchte die Hauptmethode mit Reflektion aufrufen
Verwenden Sie die Standardmethode der Schnittstelle ordnungsgemäß?
Ich möchte die Protokollausgabe unter Android vereinfachen
Was tun, wenn Sie die Listen-Adresse im Einstellungsbildschirm des Dockers Mattermost ändern?
Wenn Sie den Wortlaut ändern möchten, der angezeigt werden soll, wenn Sie ein Auswahlfeld aus enum erstellen
Was tun, wenn Sie Ruby mit rbenv installiert haben, die Version sich jedoch nicht ändert?
Notiz, die zum Anmeldebildschirm übergeht, wenn Sie nicht mit devise angemeldet sind
Wenn Sie die Hauptzweiginformationen in dem aktuellen Zweig wiedergeben möchten, an dem Sie gerade arbeiten
Was ist, wenn die Ergebnisse von Summe und Injektion (: +) unterschiedlich sind?
[Rails] [Bootstrap] Ich möchte die Schriftgröße entsprechend ändern
Wenn Sie Programmieren an der Universität studieren möchten, gehen Sie nach Australien
Ich möchte Tomcat auf den Server bringen und die Anwendung starten
[# 3 Java] Lesen Sie dies, wenn Sie Java lernen möchten! ~ Sorgfältig ausgewählt ~
Ich möchte die Protokollausgabeeinstellung von UtilLoggingJdbcLogger ändern
Android App, die Bilder aus der Galerie auswählt und anzeigt
Sie verwenden derzeit Java 6. Lösung in Android Studio Gradle
Wenn Sie explizit ODER oder UND mit Ransack schreiben möchten
[Android] Ändern Sie den App-Namen und das App-Symbol für jeden Geschmack
Was tun, wenn der Befehl Rails unbrauchbar wird?
[Swift] Wenn Sie wissen möchten, ob die Anzahl der Zeichen in String mit einer bestimmten Anzahl übereinstimmt ...
[Für Super-Anfänger] Das Mindestwissen, das Sie mit Hashes und Symbolen berücksichtigen möchten
Wenn Sie Ihre Container nur in der Cloud ausführen möchten, ist Azure Container Instances ganz einfach