Following the official documents guide: Realm Relationships
I tried to write some code of Many-to-One relationship, and I found some things that are not consistent.
Two POJO Contace.java and Email.java are defined as follows:
public class Email extends RealmObject {
private String address;
private boolean active;
// ... setters and getters left out
}
public class Contact extends RealmObject {
private String name;
private Email email;
// ... setters and getters left out
}
Situation 1: Create a normal Email object and assign to different Contact contactA and contactB.
Email email = new Email();
email.setAddress("realm_test#gmail.com");
email.setActive(true);
Contact contactA = new Contact();
contactA.setName("Bear");
contactA.setEmail(email);
Contact contactB = new Contact();
contactB.setName("Monkey");
contactB.setEmail(email);
realm.beginTransaction();
realm.copyToRealm(contactA);
realm.copyToRealm(contactB);
realm.commitTransaction();
After I call realm.copyToRealm(), two Email objects will be created here. So, when I query Contact from Realm, one Email object will become two different object? I think this is not a Many-to-One relationship anymore, it just become One-to-One relationship.
Situation 2: Call realm.createObject() to create a proxy Email object and two proxy Contact object, and assign email to contactA and contactB.
realm.beginTransaction();
Email email = realm.createObject(Email.class);
email.setAddress("realm_test#gmail.com");
email.setActive(true);
Contact contactA = realm.createObject(Contact.class);
contactA.setName("Bear");
contactA.setEmail(email);
Contact contactB = realm.createObject(Contact.class);
contactB.setName("Monkey");
contactB.setEmail(email);
realm.commitTransaction();
Here we can see just one Email object in the table, and that's what I expected, just as described in the document above.
So, why is there no consistency in situation1 and situation2? Is it a bug in situation1? Or am I missing something?
Realm version(s):0.88.3
Android Studio version:2.0
Looking forward to your reply! Thanks!
Thanks for beeender's answer here: [issue-2730]
This is the expected behaviour.
In the situation1, the email you set to contactA and contactB is a standalone object which is not managed by Realm. So when you copy it to Realm, Realm has no way to know you mean they are the same object. To solve this, you can add a #PrimaryKey to Email and then use copyToRealmOrUpdate. Realm will try to detect if you mean the same email object for both contactA and contactB based on the primary key.
In the situation2, since the email object is managed by Realm, when you call setters, Realm knows that email is actually the same one.
Finally I set a #PrimaryKey both in Contact and Email, as following:
public class Email extends RealmObject {
#PrimaryKey
private String address;
private boolean active;
// ... setters and getters left out
}
public class Contact extends RealmObject {
#PrimaryKey
private String name;
private Email email;
// ... setters and getters left out
}
Then change copyToRealm() to copyToRealmOrUpdate() on contactA and contactB.
realm.beginTransaction();
realm.copyToRealmOrUpdate(contactA);
realm.copyToRealmOrUpdate(contactB);
realm.commitTransaction();
And the relationships betweens Contact and Email was correctly in situation 1.
In the first case two email is created because you are doing copyToRealm two times on different instance.
From the docs Each contact (instance of Contact) have either 0 or 1 email (instance of Email)
Related
I am searching everywhere but not found the exact way to use Inner Join on Realm DB in Android, check my code
public class UserName extends RealmObject {
private int UserID;
private String UserName;
// Getter Setters
}
public class UserDepartment extends RealmObject {
private int UserID;
private String UserDepartment;
// Getter Setters
}
In SQLite by using following query we easily get data
Select n.UserName, d.UserDepartment
FROM Name n INNER JOIN Department d
ON n.UserID=d.UserID
ORDER BY d.UserDepartment
But how to do same thing on Realm DB to get result...
Thanks in Advance!!
As you may know Realm is a non-relational database and concepts like join belongs for relational database. but if you need to have both UserName and UserDepartment in a single model class there are lots of implementations. but due to my experiences and according to realm constraints in using objects on different threads and updating objects snapshot with realm file, I suggest you to create a new entity, just like this:
class User extends RealmObject{
private int userId;
private UserName username;
private UserDepartment userDepartment;
}
whenever you insert a record into either UserName or UserDepartment you need to insert a record or update existing record in User.
When using the android data-persistent library Android Room ,how can I directly insert the Comment Object into the database including all the field value, and how can I query all the value out as a Comment Object?
As I know, I can not use the Comment Object as a Entity in Room directory, because of the field replyComment is also a Comment Object. And I can not query out a Comment Object even I define a POJO using the #Relations annotation either because of the one-to-one relations and one-to-many relations all included in the Comment Object.
Is there any other way except changing the Comment Model definition, such as using foreign key, making a effect on insert action and query action?
public class Comment {
public String content;
public String id;
public Comment replyComment;
public User user;
public List<ImageMedia> images;
}
public class User{
public String id;
public String name;
}
public class ImageMedia{
public String key;
public String url;
}
Is there any other way except changing the Comment Model definition
No. You would need to create a set of entities that model the database structure, where children have foreign key columns pointing back to their parents:
CommentEntity has a foreign key back to CommentEntity for the reply
CommentEntity has a foreign key back to UserEntity
ImageMediaEntity has a foreign key back to CommentEntity
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've been trying to add Realm in my Android app. Their docs are pretty well explained & easy to follow. But it fails to explain this one particular area. I'm unable to figure out the practical use for the #Ignore annotation. I know that fields under this annotation are not persisted.
Can someone please share a few use cases. Also I wanted to know the scope of such fields. I mean, if I set an #Ignore field to some value, would that value be available to the other classes in my app for that particular launch session. If yes, then how do we access it? If no (which I guess is the case), then why do we need such a field anyway?
I've searched here and on web but couldn't find the relevant information. If out of my ignorance, I've missed upon some resource, please guide me to it.
Thanks.
Accordingly to the official documentation (see https://realm.io/docs/java/latest/) #Ignore is useful in two cases:
When you use GSON integration and your JSON contains more data than you want to store, but you still would like to parse it, and use right after.
You can't create custom getters and setter in classes extending RealmObject, since they are going to be overridden. But in case you want to have some custom logic anyway, ignored fields can be used as a hack to do that, because Realm doesn't override their getter & setters. Example:
package io.realm.entities;
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
public class StringOnly extends RealmObject {
private String name;
#Ignore
private String kingName;
// custom setter
public void setKingName(String kingName) { setName("King " + kingName); }
// custom getter
public String getKingName() { return getName(); }
// setter and getter for 'name'
}
Ignored fields are accessible only from the object they were set in (same as with regular objects in Java).
UPDATE: As the #The-null-Pointer- pointed out in the comments the second point is out of date. Realm now allows having custom getters and setters in Realm models.
Here's a couple of real-world use cases:
1 - Get user's fullname:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private String fullName;
public String getFullName() {
return getFirst() + " " + getLast();
}
Get JSON representation of object:
public class User extends RealmObject {
private String first;
private String last;
#Ignore
private JSONObject Json;
public JSONObject getJson() {
try {
JSONObject dict = new JSONObject();
dict.put("first", getFirst());
dict.put("last", getLast());
return dict;
} catch (JSONException e) {
// log the exception
}
return null;
}
I've found it useful to define field names for when I am querying. For example
User.java
public class User extends RealmObject {
#Index
public String name;
#Ignore
public static final String NAME = "name";
}
And then later on I can do something like:
realm.where(User.class).equalTo(User.NAME, "John").findFirst();
This way if the schema changes from say name to id I don't have to hunt down every occurrence of "name".
Please see the the official documentation about #Ignore annotation:
The annotation #Ignore implies that a field should not be persisted to disk. Ignored fields are useful if your input contains more fields than your model, and you don’t wish to have many special cases for handling these unused data fields.
I have a Person object and it has a RealmList of phone numbers.
How can I search a person by a phone number? I'm using Realm for my Android project.
Here are my models:
class Person extends RealmObject{
private RealmList<Contact> emails;
private RealmList<Contact> phones;
}
class Contact extends RealmObject{
private String type;
private String value;
}
I need to search a person by email and phone.
Thanks for any suggestion!
You can chain properties with '.'
So in your case you could do:
realm.where(Person.class).equalTo("phones.value", whateverYouWant).findAll()
Obviously you would replace 'whateverYouWant' with the value you want to search with.
Hope that helps.