web-dev-qa-db-fra.com

La migration de la base de données de salle ne gère pas correctement la migration ALTER TABLE

Java.lang.IllegalStateException

La migration n'a pas correctement géré l'utilisateur (therealandroid.github.com.roomcore.Java.User).

Attendu:

TableInfo {name = 'user', columns = {name = Column {name = 'name', type = 'TEXT', notNull = false, primaryKeyPosition = 0}, age = Column {name = 'age', type = 'INTEGER ', notNull = true, primaryKeyPosition = 0}, id = Column {name =' id ', type =' INTEGER ', notNull = true, primaryKeyPosition = 1}}, foreignKeys = []} Trouvé:

A trouvé

TableInfo {name = 'user', columns = {name = Column {name = 'name', type = 'TEXT', notNull = false, primaryKeyPosition = 0}, id = Column {name = 'id', type = 'INTEGER ', notNull = true, primaryKeyPosition = 1}, age = Column {name =' age ', type =' INTEGER ', notNull = false, primaryKeyPosition = 0}}, foreignKeys = []}

J'essaie d'effectuer une migration simple, j'ai une classe appelée Useret elle a deux colonnes ID (primary key) et NAME TEXT puis je remplis la base de données avec deux données utilisateurs, puis j'ajoute la colonne AGE dans l'objet User et dans la constante de migration j'ajoute un alter table pour ajouter cette nouvelle colonne et enfin je remplace la version de la base de données 1 à 2.

Voici le code

User.class

@Entity(tableName = "user")
  public class User {

  @PrimaryKey
  private int id;

  @ColumnInfo(name = "name")
  private String name;

  @ColumnInfo(name = "age")
  private int age;


  public int getId() {
      return id;
  }

  public void setId(int id) {
      this.id = id;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public int getAge() {
      return age;
  }

  public void setAge(int age) {
      this.age = age;
  }
}

Classe de base de données

@Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
    abstract UserDao userDao();
}

Code de migration

public static Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
    }
 };

et ça appelle

Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
            .addMigrations(MIGRATION_1_2)
            .allowMainThreadQueries()
            .build();

Avant de changer l'objet en ajoutant AGE et en effectuant la migration j'ajoute deux registres et ça marche.

Après avoir effectué la migration, j'ai juste essayé d'ajouter un nouvel utilisateur comme ci-dessous:

  User user = new User();
  user.setName("JoooJ");
  user.setId(3);
  user.setAge(18);

  List<User> userList = new ArrayList<>();
  userList.add(user);
  App.database(this).userDao().insertAll(userList);  // The crash happens here

Autres renseignements:

Android Studio 3 et je n'ai pas testé dans la réalité.

Dépendances:

compile "Android.Arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "Android.Arch.persistence.room:compiler:1.0.0-alpha9-1"

compile "Android.Arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3

Quelqu'un peut-il m'aider s'il vous plaît, je ne sais vraiment pas ce que je fais mal ou si c'est un bug.

24
diogojme

Le message d'erreur est difficile à analyser, mais il y a une différence:

TableInfo {name = 'user', columns = {name = Column {name = 'name', type = 'TEXT', notNull = false, primaryKeyPosition = 0}, age = Column {name = 'age', type = 'INTEGER ', notNull = true, primaryKeyPosition = 0}, id = Column {name =' id ', type = 'INTEGER', notNull = true, primaryKeyPosition = 1}}, foreignKeys ]} Trouvé:

A trouvé

TableInfo {name = 'user', columns = {name = Column {name = 'name', type = 'TEXT', notNull = false, primaryKeyPosition = 0}, id = Column {name = 'id', type = 'INTEGER ', notNull = true, primaryKeyPosition = 1}, age = Column {name =' age ', type =' INTEGER ', == [- notNull = false, primaryKeyPosition = 0}}, foreignKeys = []}

L'âge est nul, mais Room s'attendait à ce qu'il ne soit pas nul.

Modifiez votre migration en:

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");

Puisque cette explication d'exception est TRÈS difficile à analyser, j'ai créé n petit script qui fait la différence pour vous.

Exemple:

mig "Java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=[]} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=[]}"

Résultat:

expected/found diff

47
Benoit Duffez

J'ai aussi écrit un petit script JS que vous pouvez trouver https://hrankit.github.io/RoomSQLiteDifferenceFinder/

Le processus est assez simple.

  1. Entrez le journal des erreurs attendues dans la colonne attendue qui est celle de gauche.

  2. Entrez le journal des erreurs trouvées dans la colonne Trouvé qui est la bonne.

  3. Appuyez sur Go. bouton. Les journaux d'erreurs sont convertis en JSON.

  4. Appuyez sur le bouton Comparer et Voila, vous avez la différence dont vous avez besoin.

Ce plugin découvre la différence entre les deux vidages attendus et trouvés du Android Studio Logcat.

Découvrez l'image de comparaison ici

11
HRankit

Aucune des réponses n'est correcte dans aucun des liens. Après de nombreuses expériences, a trouvé un moyen pour cela. La requête ALTER doit être écrite de la manière suivante pour la faire fonctionner:

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")

Cependant, la valeur par défaut entière peut être n'importe quoi.

Si vous souhaitez ajouter une colonne de type String, ajoutez de la manière suivante:

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")

Cela fonctionne comme un charme.

6
Prabhtej Singh

J'ai rencontré ce problème aujourd'hui, je viens de changer les champs int en Integer dans Entities. Comme int ne peut pas être null mais que les objets Integer peuvent être null.

3
Zaid Mirza

Si vous obtenez des différences notNull, vous pouvez simplement marquer votre champ de classe avec l'annotation @NonNull, ou changer votre sql avec ALTER TABLE. Mais si vous obtenez des différences de type de colonne, comme prévu: TYPE = TEXT, puis trouvé TYPE = '' (COLLATE NOCASE), ou INTEGER attendu, trouvé INT, alors la seule solution est de supprimer et de recréer votre table. Sqlite ne permet pas de modifier les types de colonnes.

Utilisez INTEGER dans Sqlite au lieu de INT et marquez votre Java avec @ColumnInfo (collate = NOCASE) (si vous utilisez NOCASE dans Sqlite).

Jetez un œil au fichier json sous app\schemas pour obtenir le sql pour les requêtes attendues.

static final Migration MIGRATION_2_3= new Migration(2, 3) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {

            database.execSQL("DROP TABLE IF EXISTS table_tmp");

            database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

            database.execSQL("insert into table_tmp (`id`, `name` , ...");

            database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

            database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

            database.execSQL("DROP TABLE IF EXISTS table");

            database.execSQL("alter table table_tmp rename to table");

        }
    };
1
live-love