127 votes

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

Laisse pas supposer, j'ai une Chambre simple base de données:

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

Maintenant, je vais ajouter une nouvelle entité: Pet et se cogner la version 2:

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

Bien sûr, Chambre déclenche une exception: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

En supposant, je n'ai pas changé d' User de la classe (donc toutes les données est sûr), je dois fournir la migration, ce qui crée une nouvelle table. Donc, je suis à la recherche dans les classes générées par la Chambre, à la recherche pour la requête générée pour créer ma nouvelle table, le copier et le coller 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 ça gênant de le faire manuellement. Est-il un moyen de dire à la Chambre: je ne suis pas toucher la table existante, de sorte que les données sont en sécurité. S'il vous plaît créer de la migration pour moi?

107voto

musooff Points 1038

Salle de ne PAS avoir un bon Système de Migration, au moins pas jusqu'à ce qu' 2.1.0-alpha03.

Il est prévu d'avoir une meilleure Migration du Système en 2.2.0

Donc, jusqu'à ce que nous avons plus de Système de Migration, il y a quelques solutions de contournement pour avoir facilement des Migrations dans la Chambre.

Comme il n'y a pas une telle méthode @Database(createNewTables = true) ou MigrationSystem.createTable(User::class), ce qui devrait être l'un ou l'autre, la seule voie possible est en cours d'exécution

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

à l'intérieur de votre migrate méthode.

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 au-dessus de SQL script, vous avez 4 possibilités

1. Écrire par vous-même

Fondamentalement, vous devez écrire le script ci-dessus qui correspond le script de la Pièce génère. De cette façon, est possible, pas possible. (Considérez que vous avez 50 champs)

2. L'Exportation De Schéma

Si vous incluez exportSchema = true à l'intérieur de votre @Database d'annotation, Salle de générer 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 ci-dessous les lignes en build.grade de votre application module

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

Lorsque vous exécutez ou de construire le projet, vous obtiendrez un fichier JSON 2.json, qui a toutes les requêtes à l'intérieur de votre Chambre de la base de données.

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

Donc, vous pouvez inclure le ci-dessus createSql l'intérieur de vous - migrate méthode.

3. Requête Get à partir de AppDatabase_Impl

Si vous ne voulez pas exporter le schéma, vous pouvez toujours obtenir la requête en cours d'exécution ou de la construction du projet qui va générer AppDatabase_Impl.java le fichier. 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`))");

Au sein d' createAllTables méthode, il faut créer des scripts de toutes les entités. Vous pouvez l'obtenir et de l'inclure dans l'intérieur de vous - migrate méthode.

4. Annotation Processing.

Comme vous pouvez le deviner, Chambre génère tous les ci-dessus mentionné schema, et AppDatabase_Impl fichiers dans le temps de compilation et avec l'Annotation de Traitement qui vous ajoutez à

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

Cela signifie que vous pouvez également en faire de même et de faire votre propre annotation de bibliothèque de traitement qui génère tout le nécessaire de créer des requêtes pour vous.

L'idée est de faire une annotation de traitement de la bibliothèque de la Chambre des annotations de l' @Entity et @Database. Prendre une classe qui est annotée avec l' @Entity par exemple. Ce sont ces étapes que vous devrez suivre

  1. Faire un nouveau StringBuilder et l'option "CREATE TABLE if not EXISTS "
  2. Obtenir le nom de la table, soit à partir d' class.simplename ou tableName domaine de l' @Entity. Ajouter à votre StringBuilder
  3. Ensuite, pour chaque domaine de votre classe, de créer des colonnes de SQL. Prendre le nom, le type, la possibilité de valeur null de la zone par le champ lui-même ou par @ColumnInfod'annotation. Pour chaque champ, vous devez ajouter id INTEGER NOT NULL style d'une colonne à votre StringBuilder.
  4. Ajouter des clés primaires en @PrimaryKey
  5. Ajouter ForeignKey et Indices s'il existe.
  6. Après la fin de la convertir en chaîne et en enregistrer une nouvelle classe que vous souhaitez utiliser. Par exemple, l'enregistrer 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)
    }
}

J'ai fait une bibliothèque pour moi-même que vous pouvez consulter, et même l'utiliser dans votre projet. Notez que la bibliothèque que j'ai fait n'est pas complète et il répond à mes besoins pour la création de la table.

RoomExtension pour une meilleure Migration

Application qui utilise RoomExtension

Espérons que cela a été utile.

4voto

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.

0voto

Larry Stent Points 39

Vous pouvez ajouter le suivant gradle commande à votre defaultconfig.ini.php dans votre application.gradle:

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

Lorsque vous exécutez cette compiler une liste de noms de table avec leurs instructions CREATE TABLE à partir de laquelle vous pouvez simplement copier et coller dans votre migration des objets. Vous pourriez avoir à changer les noms de table.

Par exemple c'est à partir de mon schéma généré:

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

Et j'ai donc copier coller le createSql déclaration et de modifier le " ${TABLE_NAME}' à 'actifs' le nom de la table, et le tour est joué générés automatiquement, Salle de créer des instructions.

-3voto

user1730694 Points 331

Peut-être que dans ce cas (si vous avez uniquement créé une nouvelle table sans en modifier d'autres), vous pouvez le faire sans créer de migration du tout?

-3voto

Sujeet Kumar Points 133

Vous pouvez faire de cette façon-

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

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

Restant sera le même que vous avez mentionné ci-dessus-

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

Référence - Pour plus

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X