Search code examples
androidmigrationrealmdatabase-migration

Realm Migration - initialize new primary key as int


I our app, we added a new primary key to one of our elements (this was quite a while ago, actually). So naturally, a migration was needed. The problem is, it's pretty much impossible to test, because nobody can really tell, how to produce those objects in the first place (and intellij doesn't provide any answers either, for whatever reason)

Anyway, here's my migration-code:

public class CustomMigration implements RealmMigration{

    private int currentKey = 0;

    public void migrate(DynamicRealm realm, long oldVersion, long newVersion){
        RealmSchema schema = realm.getSchema();
        if(oldVersion <= 4){}
            if(schema.contains("AvailableCandidate"){
                if(!schema.get("AvailableCandidate").hasField("pos")){
                    .addField("pos", int.class, FieldAttribute.PRIMARY_KEY)
                        .transform(new RealmObjectSchema.Function() {
                            @Override
                            public void apply(DynamicRealmObject obj) {
                                obj.setInt("pos", currentKey++);
                            }
                        });
                }
            }
            //
            //  here be more code
            //
            oldVersion = 5;
        }
    }
}

pay special attention to the variable currentKey. I figured that transform would work like an iterator and currentKey should be incremented every time transform would iterate.

Problem is, there still are users that seem to get that bug and seemingly, currentKey is not incremented.

What's the solution to this nasty problem?

Edit: the exception that fabric spit out is the following:

"pos" cannot be a primary key, it already contains duplicate values: 0

Solution

  • You should only add primary key constraint once the values inside the field don't violate the constraint.

    public class CustomMigration implements RealmMigration{
    
        private int currentKey = 0;
    
        public void migrate(DynamicRealm realm, long oldVersion, long newVersion){
            RealmSchema schema = realm.getSchema();
            if(oldVersion <= 4){}
                if(schema.contains("AvailableCandidate"){
                    if(!schema.get("AvailableCandidate").hasField("pos")){
                        .addField("pos", int.class, FieldAttribute.INDEXED)
                        .transform(new RealmObjectSchema.Function() {
                            @Override
                            public void apply(DynamicRealmObject obj) {
                                obj.setInt("pos", currentKey++);
                            }
                        })
                       .addPrimaryKey("pos");
                    }
                }
                //
                //  here be more code
                //
                oldVersion = 5;
            }
        }
    
        @Override
        public boolean equals(Object obj) {
             if(obj == null) {
                 return false;
             }
             return CustomMigration.class.equals(obj.getClass());
        }
    
        @Override
        public int hashCode() {
             return CustomMigration.class.hashCode();
        }
    }