web-dev-qa-db-fra.com

Migration de la base de données de la salle si seule une nouvelle table est ajoutée

Ne supposons pas, j'ai une simple base de données de salle:

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

Maintenant, j'ajoute une nouvelle entité: Pet et bosse la version à 2:

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

Bien sûr, Room lève une exception: Java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

En supposant que je n’ai pas changé de classe User (donc que toutes les données sont en sécurité), je dois fournir une migration qui crée simplement une nouvelle table. Je cherche donc dans les classes générées par Room, cherche la requête générée pour créer ma nouvelle table, la copie et la colle dans la migration:

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

Cependant, je trouve cela peu pratique de le faire manuellement. Y a-t-il un moyen de dire à Room: Je ne touche à aucun des tableaux existants, les données sont donc protégées. Créez la migration pour moi?

Room [~ # ~] pas [~ # ~] possède un bon système de migration, du moins pas avant 2.1.0-alpha03.

Il est prévu que le système de migration sera meilleur dans 2.2.0

Donc, jusqu'à ce que nous ayons un meilleur système de migration, certaines solutions de contournement facilitent la migration dans la salle.

Comme il n’existe pas de méthode telle que @Database(createNewTables = true) ou MigrationSystem.createTable(User::class), qu’il devrait y en avoir une ou une autre, le seul moyen possible est de courir

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

dans votre méthode 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`))")
    }
}

Afin d'obtenir plus haut [~ # ~] sql [~ # ~] script, vous avez 4 moyens

1. Écrivez par vous-même

Fondamentalement, vous devez écrire le script ci-dessus qui correspondra au script généré par Room. Cette voie est possible, non réalisable. (Considérez que vous avez 50 champs)

2. Schéma d'exportation

Si vous incluez exportSchema = true Dans votre annotation @Database, Room générera un schéma de base de données dans/schemas de votre dossier de projet. L'utilisation est

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

Assurez-vous que vous avez inclus les lignes ci-dessous dans build.grade De votre module d'application.

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

Lorsque vous exécutez ou générez le projet, vous obtenez un fichier JSON 2.json, Contenant toutes les requêtes de votre base de données Room.

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

Ainsi, vous pouvez inclure la méthode createSql ci-dessus _ dans la méthode migrate.

3. Obtenez une requête de AppDatabase_Impl

Si vous ne souhaitez pas exporter le schéma, vous pouvez toujours obtenir la requête en exécutant ou en générant le projet qui générera le fichier AppDatabase_Impl.Java. et dans le fichier spécifié, vous pouvez avoir.

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

Dans la méthode createAllTables, il y aura les scripts de création de toutes les entités. Vous pouvez l'obtenir et l'inclure dans votre méthode migrate.

4. Traitement des annotations.

Comme vous pouvez le deviner, Room génère tous les fichiers schema et AppDatabase_Impl Mentionnés ci-dessus dans le délai de compilation et avec le traitement des annotations que vous ajoutez avec

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

Cela signifie que vous pouvez également faire de même et créer votre propre bibliothèque de traitement des annotations, qui génère toutes les requêtes de création nécessaires.

L'idée est de créer une bibliothèque de traitement des annotations pour les annotations de pièce de @Entity Et @Database. Prenez un cours annoté avec @Entity Par exemple. Ce sont les étapes que vous devrez suivre

  1. Créez un nouveau StringBuilder et ajoutez "CREATE TABLE IF NOT EXISTS"
  2. Obtenez le nom de la table à partir de class.simplename Ou par le champ tableName de @Entity. Ajoutez-le à votre StringBuilder
  3. Créez ensuite des colonnes de SQL pour chaque champ de votre classe. Prenez le nom, le type, la nullité du champ, soit par le champ lui-même, soit par l'annotation @ColumnInfo. Pour chaque champ, vous devez ajouter le style id INTEGER NOT NULL D'une colonne à votre StringBuilder.
  4. Ajouter des clés primaires par @PrimaryKey
  5. Ajoutez ForeignKey et Indices s'il existe.
  6. Après avoir terminé, convertissez-le en chaîne et enregistrez-le dans une nouvelle classe que vous souhaitez utiliser. Par exemple, enregistrez-le comme ci-dessous
public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

Ensuite, vous pouvez l'utiliser comme

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

Je me suis créé une telle bibliothèque que vous pouvez consulter et même l’utiliser dans votre projet. Notez que la bibliothèque que j'ai créée n'est pas pleine et qu'elle répond simplement à mes exigences en matière de création de table.

RoomExtension pour une meilleure migration

Application qui utilise RoomExtension

J'espère que c'était utile.

32
musooff

Désolé, Room ne prend pas en charge la création automatique de tables sans perte de données.

Il est obligatoire d'écrire la migration. Sinon, toutes les données seront effacées et la nouvelle structure de table sera créée.

2