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
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)
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
.
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
.
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
StringBuilder
et ajoutez "CREATE TABLE IF NOT EXISTS"class.simplename
Ou par le champ tableName
de @Entity
. Ajoutez-le à votre StringBuilder
@ColumnInfo
. Pour chaque champ, vous devez ajouter le style id INTEGER NOT NULL
D'une colonne à votre StringBuilder
.@PrimaryKey
ForeignKey
et Indices
s'il existe.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.
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.