I'm running into an issue with Realm, one to many relationship and updating existing entries to add more data to the relationship.
Model 1
public class Model1 extends RealmObject
{
#PrimaryKey
private String identifier;
#SerializedName("type")
private String type;
#SerializedName("additionalInfo")
private String additionalInfo;
#SerializedName("options")
private RealmList<Model2> moreModels;
}
Model 2
public class Model2 extends RealmObject
{
#SerializedName("hint")
private String hint;
#SerializedName("label")
private String label;
#SerializedName("favorite")
private boolean favorite;
#PrimaryKey
private String identifier;
}
So from an API I'm getting a list of Model1 objects, and each of those objects contains their own list of Model2 objects. Simple enough. This works well, I can add it to Realm and see the relationship.
Now my problem emerges when I make a second API call for a different user. The way I was hoping to have this work was to have the identifier property on Model2 be made up of userId + label. Therefore, each user will have their own set of Model2 objects. However, I was hoping to have only one set of Model1 objects, where its reference to Model2 objects gets updated as more are added to the Model2 table.
Instead what I got working is I keep my one set of Model1 (good), but the relationship to Model2 always gets overwritten with the latest set. So I have a table of Model2 objects that has 80 entries, but only the last 40 are connected.
So it looks like my update or insert is updating the Model1 entries in the table (GOOD) but instead of concatenating the existing relationships with the new ones, its just updating the relationships to only use the new ones. The Model2 entries are being added to their table correctly in that there are not duplicates based on the primary key. But the connection is broken.
Update Code
List<Model1> test = response.body();
try(Realm realmInstance = Realm.getDefaultInstance()) {
realmInstance.executeTransaction((realm) ->
{
realm.insertOrUpdate(test);
final RealmResults<Model> category = realm.where(Model1).findAll();
}
);
}
Yes, that is how the insertOrUpdate and copyToRealmOrUpdate methods works currently. Ideally, there would be a mode you could provide like REPLACE_LISTS or MERGE_LISTS, but right now that hasn't been implemented.
Your only choice is manually updating these relationships.
Related
Thanks in advance.
I have scenario where i wanted to check the data difference between existing and new realm model object.
Example
public class PostModel extends RealmObject {
#Required
#PrimaryKey
#Index
private String postId;
private String message;
}
Let say we have two objects
Old
PostModel old = new PostModel("one", "Welcome");
realm.copyToRealm(old);
New Object
PostModel newOne = new PostModel("one", "Welcome to World");
before updating the old object with newOne should check data change, if change is there then should insert in the realm, like below
realm.dirtyCheckAndUpdate(old, newOne);
//underlying it should do below
Getting the record with id "one"
Check the difference between db record and new record (!old.message.equalsIgnore(newOne.message)).
if change is there then copyToRealmOrUpdate() should happen.
I just gave an example, i need to to this for complex RealmModel with relationship.
Why do you need to check? You can just call copyToRealmOrUpdate()? It will update data regardless, but if it overrides the data with the same data the end result is the same.
Otherwise, you will be forced to implement all the checking yourself, which is time-consuming and error-prone. You could also make your own annotation processor that generated the logic for you. It would look something like:
public boolean compare(PostModel m1, PostModel m2) {
if (!m1.getId().equals(m2.getId()) return false;
if (!m1.getMessage().equals(m2.getMessage()) return false;
if (!PostModelReference.compare(m1.getRef(), m2.getRef()) return false; // Recursive checks
}
I have just started to use RealmDB and cannot figure out how to save linked object correctly, to implement a sort of foreign key
Here is my main User model.
public class UserModel extends RealmObject {
#PrimaryKey
public Long id;
public String firstName;
public String lastName;
public UserSettings userSettingsModel;
}
UserSettings Model is defined as follows.
public class UserSettingsModel extends RealmObject {
#PrimaryKey
private Long id;
public String email;
public RealmList<Car> cars;
}
And Car is a model itself.
public class Car extends RealmObject {
#PrimaryKey
private Long id;
public String model;
}
The problem is that when I am trying to save UserModel it tries to recreate all objects assigned to it. So before I saving user model I have already creates some Car objects.
I don't need to create them, but to reference like the foreign key in SQL databases. And when I am retrieving a user from the database it should automatically load all related data by primary keys.
Is it possible to achieve such behavior using Realm ?
Thanks for help. Solved this problem by using the copyToRealmOrUpdate method instead of copyToRealm.
You should create a managed object using realm.createObject(clazz, pkValue); if it doesn't exist yet, then set it as value or add it to the RealmList that you get another managed object.
You can also create managed objects from unmanaged objects with copyToRealmOrUpdate() (if the object has a primary key).
And when I am retrieving a user from the database it should automatically load all related data by primary keys.
The RealmList allows access to the related objects, and in fact, is also queryable by calling .where() on it. However, this is not based on primary keys. That feature is tracked under "computed fields".
I have the following 3 classes:
1) TvShow
#Entity
public class TvShow{
#Id
private String uuid;
private String url;
private String title;
...
#ToMany(referencedJoinProperty = "tvShowUuid")
private List<Episode> episodes;
2) Episode
#Entity
public class Episode{
#Id
private String uuid;
#ToMany(referencedJoinProperty = "episodeUuid")
private List<Moment> moments;
//FK
private String tvShowUuid;
3) Moment
#Entity
public class Moment{
#Id
private String uuid;
...
private String episodeUuid;
As you can see the relationship between them is: TvShow --> oneToMany --> Episode --> oneToMany --> Moment
Assuming I have a fully populated TvShow object (called tvShow), I am doing the following to add that object to my database:
final long rowId = daoSession.getTvShowDao().insertOrReplace(tvShow);
This correctly adds the tvShow object to the TvShow table. However, the Episode and Moment tables are NOT populated at all. The tvShow object has several Episodes and each episode has several moments. I was expecting the other 2 tables to contain that data as well, but they don't.
What am I doing wrong? Am I supposed to loop through each Episode (and in turn loop through each Moment of each episode) and insert them manually instead? =(
Maybe I will be wrong, I have had the same problem, but greenDao is not able to persist cascade data - If you say insertOrReplace(tvShow) it will not persist list of episodes and list of moments. In order to perform that you have to persist all data separately.
For example:
TvShowDao.save(tvShows) OR TvShowDao.insertOrUpdate(tvShows);
AND
EpisodesDao.save(episodes) OR EpisodesDao.save(episodes)
AND
MomentDao.save(moments); OR MomentDao.save(moments);
In other words, you have to persist all data separately.
I hope so that this helped you.
In this example, the docs talked about getting the parent objects while specifying queries for the child objects.
Is there a way for getting the child objects while specifying a query for the parent object?
In the given example, can I search for dogs who are of brown color with the user named John?
EDIT: Since Realm 3.5.0, you can actually use the "backlinks" mentioned in the comment section. Rejoice!
In fact, since Realm 3.0.0, bidirectional links are a performance bottleneck, so using backlinks is the preferred way.
The way it works is:
public class User extends RealmObject {
private RealmList<Dog> dogs;
}
public class Dog extends RealmObject {
#LinkingObjects("dogs")
private final RealmResults<User> owners = null;
}
Now you can do:
realm.where(Dog.class).equalTo("color", "Brown").equalTo("owners.name", "John").findAll();
OLD ANSWER:
You can only search for dogs with a given user if you have an object link to the User.
public class Dog extends RealmObject {
//...
private User user;
}
Then you could do
realm.where(Dog.class).equalTo("color", "Brown").equalTo("user.name", "John").findAll();
How do I create OR update a ForeignCollection in OrmLite?
If I try to simply add an object to a ForeignCollection, the add method acts as a create (insert into) method, but if the object already exists I will get an error about not having a unique primary key. I don't want duplicates to appear with autoincrementing primary keys, so this is fine to get this notice.
If I use the update method, then it will error if there is nothing to update.
It seems that the foreigncollection object doesn't have a way to tell me if an object is already existing in the database.
So is the only way to write a separate query myself, see if each object exists and drop the ones that have changed?
If you are using for example :
#DatabaseTable(tableName = "question")
public class QuestionDb implements Serializable {
#ForeignCollectionField(foreignFieldName = "question", eager = true)
private ForeignCollection<AnswerDb> answers;
}
#DatabaseTable(tableName="answers")
public class AnswerDb implements Serializable{
#DatabaseField (foreign=true,canBeNull=true,columnName=FIELD_QUESTIONID)
private QuestionDb question;
}
You will have to use the function createOrUpdate of the AnswersDB.
answerYouWantToAdd.setQuestion(yourQuestion);
answerDao.createOrUpdate(answerYouWantToAdd);