[JAVA] Si vous utilisez Android Room et que vous souhaitez modifier la définition de colonne

La migration de salle n'est pas autorisée

Je développe une application Android à l'aide de "Room", qui est une bibliothèque de persistance créée par Google. Parce que c'est une bibliothèque relativement nouvelle, la fonction de migration n'est pas cool. .. .. Ce que je voulais faire était de "changer une colonne de type REAL en un type INTEGER", mais la spécification de sqlite selon laquelle "seulement l'ajout de colonnes peut être fait avec l'instruction ALTER" et un mariage exquis ont été créés et réalisés. J'ai eu du mal à le faire ... Cet article est ce mémo.

Procédure de solution

J'écrirai d'abord la procédure de solution.

  1. Changez le type de propriété de l'entité (dans mon cas, passez de Double? À Integer?)
  2. Correspond à l'erreur de construction provoquée par 1.
  3. Incrémentez la version de @ Database
  4. Écrivez le code de migration
    1. CREATE TABLE
    2. INSERT INTO ... SELECT
    3. DROP TABLE
    4. ALTER TABLE ... RENAME TO ...
  5. CREATE INDEX (s'il y a un index)

Je ne pense pas que vous puissiez très bien le comprendre à ce rythme, alors jetons un coup d'œil à chaque détail.

1. Changer le type de propriété d'une entité

Par exemple, si vous avez l'entité Product suivante

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,
    ...

Changez le Double? De ce prix en ʻInt?`, Et ainsi de suite. Eh bien, c'est naturel.

2. Répondre aux erreurs de construction

Le changement dans 1. devrait (généralement) provoquer une erreur de construction, donc écrasez-le un par un. La façon de le résoudre dépend de l'implémentation, je vais donc l'omettre ici.

3. Incrémentez la version de @ Database

Peut-être que vous définissez une classe sous-abstraite pour RoomDatabase comme ceci:

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()
        }
    }
}

Augmentez la version ici de un. Dans cet exemple, il est défini sur 2. Cela signifie définir la version de schéma de la base de données sur 2.

4. Écrivez le code de migration

C'était l'odeur la plus ennuyeuse. Room ne fait presque rien sur la migration. En conséquence, vous devez écrire du SQL brut. Puisque la spécification sqlite dit que "les colonnes ne peuvent pas être modifiées ou supprimées", la méthode consiste à définir une nouvelle table, à y transférer les données actuelles, à supprimer l'ancienne table, puis à renommer la nouvelle table à son nom d'origine. Je le ferai. Enfin, c'est un piège, mais l'index qui a été défini sur l'ancienne table lors de sa suppression est également supprimé, vous devez donc le créer après avoir créé la nouvelle table.

Tout d'abord, l'instruction CREATE TABLE, mais je pense qu'il est très gênant d'écrire à partir de zéro, alors utilisons la sortie de schéma de salle json. S'il y a la description suivante dans ʻapp / build.gradle`, les informations de schéma json seront crachées au moment de la construction.

app/build.gradle


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

Lorsque vous construisez avec ceci, les informations de schéma sont affichées sous forme de json sous ʻapp / schemas`. Le nom du fichier est * version du schéma * .json.

En regardant ce json, je pense que l'instruction CREATE TABLE est définie dans la clé createSql. Il est facile de le copier et d'apporter les modifications nécessaires.

Ainsi, le code de migration réel est le suivant. Prenant l'exemple de ʻAppDatabase.kt` plus tôt

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 `Nom de table approprié` ...")
                }
            }

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

Remplacez la version du schéma par la version actuelle.

Il sera renommé plus tard, vous pourrez donc utiliser le «nom de table approprié». À côté de CREATE TABLE

n'est-ce pas.

            val migration1to2 = object : Migration(1, 2) {
                override fun migrate(database: SupportSQLiteDatabase) {
                    database.execSQL("CREATE TABLE IF NOT EXISTS Nom de table approprié...")
                    database.execSQL("INSERT INTO Nom de table approprié SELECT*FROM Nom de la table que vous souhaitez modifier")
                    database.execSQL("DROP TABLE Le nom de la table que vous souhaitez modifier")
                    database.execSQL("ALTER TABLE Nom de table approprié RENAME TO Nom de table que vous souhaitez modifier")
                }
            }

Enfin, s'il a été indexé à l'origine, copiez-le depuis le schéma json. Il devrait y avoir createSql dans ʻindices`.

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

C'est comme ça.

Faisons tout cela en même temps, construisons-le et exécutons l'application. Si vous pouvez le modifier correctement, cela devrait fonctionner normalement.

Pourquoi est-ce si compliqué

La salle se développe encore, donc je pense qu'il y a pas mal de choses à faire. La première idée que j'ai eue était de définir une nouvelle entité dans Kotlin et de transférer les données vers cette nouvelle entité. Je l'ai essayé, mais le résultat est que l'application ne démarre pas ... Comment, ** Room crée une table à partir d'une entité uniquement dans le cas de la version de schéma 1 (c'est-à-dire la première fois), mais après cela, la table est générée. Il ne se génère pas automatiquement! ** Que se passe-t-il ...

C'est pourquoi lorsque vous utilisez Room, si vous ne concevez pas la table exactement depuis le début, ce sera gênant, oui, j'ai appris.

Recommended Posts

Si vous utilisez Android Room et que vous souhaitez modifier la définition de colonne
Si vous souhaitez modifier l'environnement de développement Java d'Eclipse
Si vous souhaitez inclure la classe parente dans @builder de Lombok
Si vous êtes nouveau sur Rails et souhaitez faire votre propre validation, arrêtez-vous à ce doigt.
Si vous souhaitez simuler une méthode dans RSpec, vous devez utiliser la méthode allow pour mock et la méthode singleton.
[Android] Comment activer / désactiver le panneau de notification à l'aide de StatusBarManager
[Rails] Comment créer une table, ajouter une colonne et changer le type de colonne
Si vous souhaitez satisfaire la couverture de test des méthodes privées dans JUnit
Si vous souhaitez modifier les colonnes de la base de données, etc.
Lorsque vous souhaitez utiliser la méthode à l'extérieur
Présentez docker à l'application que vous créez
[Java] Que faire si le contenu enregistré dans la base de données et le nom de l’énumération sont différents dans l’énumération qui reflète la définition de la base de données
Si vous souhaitez séparer le traitement Spring Boot + Thymeleaf
[Ruby on Rails] Comment changer le nom de la colonne
Changez la clé primaire de la base de données (MySQL) sur n'importe quelle colonne.
Je veux juger la gamme en utilisant le diplôme mensuel
[Rails] Comment changer le nom de colonne de la table
Si vous souhaitez utiliser Mockito avec Kotlin, utilisez mockito-kotlin
Je veux appeler la méthode principale en utilisant la réflexion
Utilisez-vous correctement la méthode par défaut de l'interface?
Je souhaite simplifier la sortie du journal sur Android
Que faire si vous modifiez l'adresse d'écoute à partir de l'écran des paramètres dans le menu fixe Mattermost
Lorsque vous souhaitez modifier le libellé à afficher lors de la création d'une zone de sélection à partir d'énumération
Que faire si vous avez installé Ruby avec rbenv mais que la version ne change pas
Mémo qui passe à l'écran de connexion si vous n'êtes pas connecté avec l'appareil
Lorsque vous souhaitez refléter les informations de la branche principale dans la branche actuelle sur laquelle vous travaillez actuellement
Et si les résultats de sum et inject (: +) sont différents?
[Rails] [bootstrap] Je souhaite modifier la taille de la police de manière réactive
Si vous souhaitez étudier la programmation à l'université, allez en Australie
Je veux amener Tomcat sur le serveur et démarrer l'application
[# 3 Java] Lisez ceci si vous voulez étudier Java! ~ Sélectionné avec soin ~
Je souhaite modifier le paramètre de sortie du journal de UtilLoggingJdbcLogger
Application Android qui sélectionne et affiche des images de la galerie
Vous utilisez actuellement Java 6. Solution dans Android Studio Gradle
Lorsque vous souhaitez écrire explicitement OR ou AND avec ransack
[Android] Modifiez le nom de l'application et l'icône de l'application pour chaque saveur
Que faire si la commande rails devient inutilisable
[Swift] Lorsque vous voulez savoir si le nombre de caractères dans String correspond à un certain nombre ...
[Pour les super débutants] Les connaissances minimales que vous voulez garder à l'esprit avec les hachages et les symboles
Si vous souhaitez simplement exécuter vos conteneurs dans le cloud, Azure Container Instances est facile