Reputation: 89
Realm 3.5.0. I get json from external system. And I need to save this json to Realm.
Here json:
{
"id": 1,
"address": "Address 1",
"contactsIds": [10, 20, 30, 40],
"name": "Person 1"
}
In my Android application:
So I create POJO for Person:
public class Person extends RealmObject {
@PrimaryKey
private int id;
@Required
private String name;
private String address;
private RealmList<RealmInt> contactsIds = new RealmList<>();
}
My POJO for RealmInt:
public class RealmInt extends RealmObject {
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
My custom json deserializer for RealmInt (by GSON):
public class RealmIntAdapterJsonDeserializer implements JsonDeserializer<RealmInt> {
@Override
public RealmInt deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
RealmInt realmInt = new RealmInt();
realmInt.setValue(json.getAsJsonPrimitive().getAsInt());
return realmInt;
}
}
Snippet that save json to Realm:
public void fromJsonToRealm(final String someJson) {
Realm realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Person person = JsonUtil.gson.fromJson(someJson, Person.class);
realm.copyToRealmOrUpdate(person);
}
});
}
Result of call method "fromJsonToRealm" is:
OK.
Table "Person" and "RealmInt" must be share with many Android devices. So I use SyncConfiguration for this. Here snippet:
private void setRealmDefaultConfiguration(SyncUser syncUser) {
SyncConfiguration config = new SyncConfiguration.Builder(syncUser, REALM_URL).build();
Realm.setDefaultConfiguration(config);
}
So now I want to update this person. I get the new json (for same Person) with updated data:
{
"id": 1,
"address": "Address new",
"contactsIds": [10, 20],
"name": "Person new"
}
I again start method "fromJsonToRealm" and as result it success update ONE record in table "Person", BUT add new 2 records in table "RealmInt". As result in table "RealmInt" I has 6 records.
But I want to update(not insert) records in table "RealmInt" when I update same object "Person".
So then I add annotation @PrimaryKey to field "value" in object "RealmInt".
But when I start again method "fromJsonToRealm" I get error:
E/REALM_JNI( 9018): jni: ThrowingException 8, The following changes cannot be made in additive-only schema mode:
E/REALM_JNI( 9018): - Primary Key for class 'RealmInt' has been added., .
E/REALM_JNI( 9018): Exception has been thrown: The following changes cannot be made in additive-only schema mode:
E/REALM_JNI( 9018): - Primary Key for class 'RealmInt' has been added.
So the question is: How I can update exist record in table "Person" when get updated json for SAME object Person?
Upvotes: 2
Views: 1991
Reputation: 81588
Adding/removing @PrimaryKey
is considered a "destructive" schema operation, so it is not allowed in sync mode.
With your current schema setup, you ought to delete the existing RealmInt
s from the Realm before you copy the new Person in.
public void fromJsonToRealm(final String someJson) {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Person person = JsonUtil.gson.fromJson(someJson, Person.class);
long personId = person.getId();
Person managedPerson = realm.where(Person.class).equalTo("id", personId).findFirst();
if(managedPerson != null) {
managedPerson.getContactIds().deleteAllFromRealm();
}
realm.copyToRealmOrUpdate(person);
}
});
}
Another option would be to implement some smarter merge logic
public void fromJsonToRealm(final String someJson) {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Person person = JsonUtil.gson.fromJson(someJson, Person.class);
long personId = person.getId();
Person managedPerson = realm.where(Person.class).equalTo("id", personId).findFirst();
if(managedPerson != null) {
boolean wasFound = false;
for(RealmInt existingInt : managedPerson.getContactIds()) {
for(RealmInt newInt : person.getContactIds()) {
if(existingInt.getValue() == newInt.getValue()) {
wasFound = true;
break;
}
}
if(!wasFound) {
existingInt.deleteFromRealm();
}
}
Iterator<RealmInt> newIntIterator = person.getContactIds().iterator();
while(newIntIterator.hasNext()) {
RealmInt newInt = newIntIterator.next();
boolean wasFound = false;
for(RealmInt existingInt : managedPerson.getContactIds()) {
if(existingInt.getValue() == newInt.getValue()) {
wasFound = true;
break;
}
}
if(!wasFound) {
newIntIterator.remove();
}
}
}
realm.copyToRealmOrUpdate(person);
}
});
}
P.S. I don't use RealmList<RealmInt>
s, I prefer to just save them as a comma separated string. Realm Core 3.0.0 will make this use-case much easier though.
Upvotes: 1