2017-10-11 4 views
1

Royaume Plate-forme: Mobile Android
Realm Version: 3.3.2
Cryptage: OuiSupprimer manuellement Realm sur la migration

J'ai une application de production. J'utilise le domaine de suppression automatique lorsque la migration est nécessaire sur mes applications v0.3.0 jusqu'à la version v0.14.0. Pour une raison quelconque, sur la version 1.4.2, je dois migrer manuellement. Et plusieurs utilisateurs ont eu une erreur parce qu'ils se sont mis à jour à partir de la version v0.14.0 et je ne gère que la migration de la version v0.14.0 à la version v0.14.2. Je suis confus si je gère la version depuis le début car je dois faire beaucoup de migration. Donc, je veux supprimer le domaine manuellement sur ma migration si l'utilisateur met à jour mes applications à partir de la version sous v0.14.0. Comment faire ça?

Je aime, mais j'ai toujours RealmMigrationNeededException

@Override 
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) { 
    RealmSchema schema = realm.getSchema(); 


    if (oldVersion == 0) { 
     //update from v0.10.5/update from v0.13.0 
     if (schema.get("User").hasField("blocks") || !schema.get("Contact").hasField("pinned")) { 
      realm.deleteAll(); 
      return; 
     } 

     addField(schema, "Message", "messageDuration", int.class); 
     addField(schema, "Message", "starred", boolean.class); 
     oldVersion++; 
    } 

    if (oldVersion == 1) { 
     if (!schema.contains("PhoneBook")) { 
      schema.create("PhoneBook") 
        .addField("phone", String.class) 
        .addPrimaryKey("phone") 
        .addField("name", String.class) 
        .addField("image", String.class); 
     } 

     if (schema.contains("RealmString")) { 
      schema.remove("RealmString"); 
     } 
     oldVersion++; 
    } 
} 

Répondre

1

Si vous utilisez la suite Proguard

-keepnames public class * extends io.realm.RealmModel 
-keep public class * extends io.realm.RealmModel { *; } 
-keepnames public class * extends io.realm.RealmObject 
-keep public class * extends io.realm.RealmObject { *; } 
-keepattributes *Annotation* 

Ensuite, utilisez le code suivant

public class AutoMigration 
     implements RealmMigration { 
    @Retention(RetentionPolicy.RUNTIME) 
    @Target(ElementType.FIELD) 
    public @interface MigrationIgnore { 
    } 

    @Retention(RetentionPolicy.RUNTIME) 
    @Target(ElementType.FIELD) 
    public @interface MigratedField { 
     FieldAttribute[] fieldAttributes(); 
    } 

    @Retention(RetentionPolicy.RUNTIME) 
    @Target(ElementType.FIELD) 
    public @interface MigratedLink { 
     Class<? extends RealmModel> linkType(); // RealmList<T extends RealmModel> is nice, but T cannot be obtained through reflection. 
    } 

    @Override 
    public int hashCode() { 
     return AutoMigration.class.hashCode(); 
    } 

    @Override 
    public boolean equals(Object obj) { 
     return obj != null && obj instanceof AutoMigration; 
    } 

    @Override 
    public void migrate(DynamicRealm realm, long oldVersion, long newVersion) { 
     RealmConfiguration realmConfiguration = realm.getConfiguration(); 

     Set<Class<? extends RealmModel>> latestRealmObjectClasses = realmConfiguration.getRealmObjectClasses(); 
     RealmSchema realmSchema = realm.getSchema(); 
     Set<RealmObjectSchema> initialObjectSchemas = realmSchema.getAll(); 

     // first we must create any object schema that belongs to model class that is not part of the schema yet, to allow links. 
     List<RealmObjectSchema> createdObjectSchemas = new LinkedList<>(); 

     // first we must check for classes that are in the schema, but are not in the configuration. 
     Set<String> modelClassNames = new LinkedHashSet<>(); 
     Map<String, Class<? extends RealmModel>> modelClassNameToClassMap = new LinkedHashMap<>(); 
     Set<String> schemaClassNames = new LinkedHashSet<>(); 
     Map<String, RealmObjectSchema> schemaClassNameToObjectSchemaMap = new LinkedHashMap<>(); 
     for(Class<? extends RealmModel> modelClass : latestRealmObjectClasses) { 
      modelClassNames.add(modelClass.getSimpleName()); // "Cat", requires `-keepnames public class * extends io.realm.RealmObject` 
      modelClassNameToClassMap.put(modelClass.getSimpleName(), modelClass); 
     } 
     for(RealmObjectSchema objectSchema : initialObjectSchemas) { 
      schemaClassNames.add(objectSchema.getClassName()); // "Cat", requires `-keepnames public class * extends io.realm.RealmObject` 
      schemaClassNameToObjectSchemaMap.put(objectSchema.getClassName(), objectSchema); 
     } 

     // now we must check if the model contains classes that are not part of the schema. 
     for(String modelClassName : modelClassNames) { 
      if(!schemaClassNames.contains(modelClassName)) { 
       // the model class is not part of the schema, we must add it to the schema. 
       RealmObjectSchema objectSchema = realmSchema.create(modelClassName); 
       createdObjectSchemas.add(objectSchema); 
      } 
     } 

     // we must check if existing schema classes have changed fields, or if they were removed from the model. 
     for(String objectClassName : schemaClassNames) { 
      RealmObjectSchema objectSchema = schemaClassNameToObjectSchemaMap.get(objectClassName); 
      if(modelClassNames.contains(objectClassName)) { 
       // the model was found in the schema, we must match their fields. 
       Class<? extends RealmModel> modelClass = modelClassNameToClassMap.get(objectClassName); 
       matchFields(realmSchema, objectSchema, modelClass); 
      } else { 
       // the model class was not part of the schema, so we must remove the object schema. 
       realmSchema.remove(objectClassName); 
      } 
     } 
     // now that we've set up our classes, we must also match the fields of newly created schema classes. 
     for(RealmObjectSchema createdObjectSchema : createdObjectSchemas) { 
      Class<? extends RealmModel> modelClass = modelClassNameToClassMap.get(createdObjectSchema.getClassName()); 
      matchFields(realmSchema, createdObjectSchema, modelClass); 
     } 
    } 

    private void matchFields(RealmSchema realmSchema, RealmObjectSchema objectSchema, Class<? extends RealmModel> modelClass) { 
     Field[] allModelFields = modelClass.getDeclaredFields(); 
     Set<String> modelFieldNames = new LinkedHashSet<>(allModelFields.length); 
     Map<String, Field> modelFieldNameToFieldMap = new LinkedHashMap<>(allModelFields.length); 
     for(Field field : allModelFields) { 
      modelFieldNames.add(field.getName()); 
      modelFieldNameToFieldMap.put(field.getName(), field); 
     } 
     Set<String> schemaFieldNames = objectSchema.getFieldNames(); // field names require `-keep public class * extends io.realm.RealmObject { *; }` 
     for(String schemaFieldName : schemaFieldNames) { 
      if(!modelFieldNames.contains(schemaFieldName)) { 
       // the model does not contain this field, so it no longer exists. We must remove this field. 
       objectSchema.removeField(schemaFieldName); 
      } 
     } 
     for(String modelFieldName : modelFieldNames) { 
      Field field = modelFieldNameToFieldMap.get(modelFieldName); 
      if(Modifier.isStatic(field.getModifiers())) { // we must ignore static fields! 
       continue; 
      } 
      if(Modifier.isTransient(field.getModifiers())) { // transient fields are ignored. 
       continue; 
      } 
      if(field.isAnnotationPresent(MigrationIgnore.class)) { 
       continue; // manual ignore. 
      } 
      Class<?> fieldType = field.getType(); 
      if(!schemaFieldNames.contains(modelFieldName)) { 
       // the schema does not contain the model's field, we must add this according to type! 
       if(isNonNullPrimitive(fieldType) || isPrimitiveObjectWrapper(fieldType) || isFieldRegularObjectType(fieldType)) { 
        objectSchema.addField(modelFieldName, fieldType); 
       } else { 
        if(fieldType == RealmResults.class) { // computed field (like @LinkingObjects), so this should be ignored. 
         //noinspection UnnecessaryContinue 
         continue; 
        } else if(fieldType == RealmList.class) { 
         // TODO: value lists in 4.0.0! 
         MigratedLink migratedLink = field.getAnnotation(MigratedLink.class); 
         if(migratedLink == null) { 
          throw new IllegalStateException("Link list [" + field.getName() + "] cannot be added to the schema without @MigratedLink(linkType) annotation."); 
         } 
         Class<? extends RealmModel> linkObjectClass = migratedLink.linkType(); 
         String linkedObjectName = linkObjectClass.getSimpleName(); 
         RealmObjectSchema linkedObjectSchema = realmSchema.get(linkedObjectName); 
         if(linkedObjectSchema == null) { 
          throw new IllegalStateException("The object schema [" + linkedObjectName + "] defined by link [" + modelFieldName + "] was not found in the schema!"); 
         } 
         objectSchema.addRealmListField(field.getName(), linkedObjectSchema); 
        } else { 
         if(!RealmModel.class.isAssignableFrom(fieldType)) { 
          continue; // this is most likely an @Ignore field, let's just ignore it 
         } 
         String linkedObjectName = field.getType().getSimpleName(); 
         RealmObjectSchema linkedObjectSchema = realmSchema.get(linkedObjectName); 
         if(linkedObjectSchema == null) { 
          throw new IllegalStateException("The object schema [" + linkedObjectName + "] defined by field [" + modelFieldName + "] was not found in the schema!"); 
         } 
         objectSchema.addRealmObjectField(field.getName(), linkedObjectSchema); 
        } 
       } 
      } 
      // even if it's added, its attributes might be mismatched! This must happen both if newly added, or if already exists. 
      if(isNonNullPrimitive(fieldType) || isPrimitiveObjectWrapper(fieldType) || isFieldRegularObjectType(fieldType)) { 
       matchMigratedField(objectSchema, modelFieldName, field); 
      } 
     } 
    } 

    private void matchMigratedField(RealmObjectSchema objectSchema, String modelFieldName, Field field) { 
     MigratedField migratedField = field.getAnnotation(MigratedField.class); // @Required is not kept alive by its RetentionPolicy. We must use our own! 
     if(migratedField != null) { 
      boolean isIndexed = false; 
      boolean isRequired = false; 
      boolean isPrimaryKey = false; 
      for(FieldAttribute fieldAttribute : migratedField.fieldAttributes()) { 
       if(fieldAttribute == FieldAttribute.INDEXED) { 
        isIndexed = true; 
       } else if(fieldAttribute == FieldAttribute.REQUIRED) { 
        isRequired = true; 
       } else if(fieldAttribute == FieldAttribute.PRIMARY_KEY) { 
        isPrimaryKey = true; 
       } 
      } 
      if(isPrimaryKey && !objectSchema.isPrimaryKey(modelFieldName)) { 
       if(objectSchema.hasPrimaryKey()) { 
        throw new UnsupportedOperationException(
          "Multiple primary keys are not supported: [" + objectSchema 
            .getClassName() + " :: " + modelFieldName + "]"); 
       } 
       objectSchema.addPrimaryKey(modelFieldName); 
      } 
      if(!isPrimaryKey && objectSchema.isPrimaryKey(modelFieldName)) { 
       objectSchema.removePrimaryKey(); 
      } 
      // index management must be after primary key because removePrimaryKey() removes index as well. 
      if((isIndexed || isPrimaryKey) && !objectSchema.hasIndex(modelFieldName)) { 
       objectSchema.addIndex(modelFieldName); 
      } 
      if(!isIndexed && !isPrimaryKey /* primary key is indexed by default! */ && objectSchema.hasIndex(modelFieldName)) { 
       objectSchema.removeIndex(modelFieldName); 
      } 
      if(isNonNullPrimitive(field.getType())) { 
       if(!objectSchema.isRequired(modelFieldName)) { 
        objectSchema.setNullable(modelFieldName, false); 
       } 
      } else { 
       if(isRequired && objectSchema.isNullable(modelFieldName)) { 
        objectSchema.setNullable(modelFieldName, false); 
       } 
       if(!isRequired && !objectSchema.isNullable(modelFieldName)) { 
        objectSchema.setNullable(modelFieldName, true); 
       } 
      } 
     } 
    } 

    private boolean isFieldRegularObjectType(Class<?> fieldType) { 
     return fieldType == String.class || fieldType == Date.class || fieldType == byte[].class; 
    } 

    private boolean isPrimitiveObjectWrapper(Class<?> fieldType) { 
     return fieldType == Boolean.class // 
       || fieldType == Byte.class || fieldType == Short.class || fieldType == Integer.class || fieldType == Long.class // 
       || fieldType == Float.class || fieldType == Double.class; 
    } 

    private boolean isNonNullPrimitive(Class<?> fieldType) { 
     return fieldType == boolean.class // 
       || fieldType == byte.class || fieldType == short.class || fieldType == int.class || fieldType == long.class // 
       || fieldType == float.class || fieldType == double.class; 
    } 
} 

Ensuite, vous annoter vos champs pour lorsque la classe de modèle a @PrimaryKey ou @Ignore Ou @Required, ou @Index pour toutes vos classes du modèle actuel

@Index 
@AutoMigration.MigratedField(fieldAttributes = {FieldAttribute.INDEXED}) 
private String name; 

@Required 
@AutoMigration.MigratedField(fieldAttributes = {FieldAttribute.REQUIRED}) 
private String ownerName; 

private Cat cat; 

@AutoMigration.MigratedLink(linkType = Cat.class) 
private RealmList<Cat> manyCats; 

Ensuite, vous pouvez faire le code suivant

if (oldVersion == 0) { 
    //update from v0.10.5/update from v0.13.0 
    // if (schema.get("User").hasField("blocks") || !schema.get("Contact").hasField("pinned")) { 
    // realm.deleteAll(); 
    // return; 
    //} 

    //addField(schema, "Message", "messageDuration", int.class); 
    //addField(schema, "Message", "starred", boolean.class); 
    //oldVersion++; 
    AutoMigration autoMigration = new AutoMigration(); 
    autoMigration.migrate(realm, oldVersion, newVersion); 
    return; // <-- !! all fields of current model class version will be added !! 
} 
if(oldVersion == 1) { 
    // manual migration 
} 

S'il vous plaît essayer avec une base de données réelle cependant, le code est légèrement expérimentale.


Autre option serait d'ouvrir le royaume avec un royaume dynamique d'abord, vérifiez son schéma, si elle est trop vieux puis supprimez le royaume, et ouvrez-le avec la migration spécifiée par la suite.

DynamicRealm dynRealm = DynamicRealm.getInstance(config); 
if(/* check schema like in migration*/) { 
    dynRealm.close(); 
    Realm.delete(config); 
} 
if(!dynRealm.isClosed()) { 
    dynRealm.close(); 
} 
Realm realm = Realm.getInstance(config); 
+1

Merci. J'utilise la deuxième option^_ ^ –