I started trying Realm for Android, so I created two classes:
class ProductSelection extends RealmObject {
private String selectedProductName;
private String selectedProductID;
…
}
class ProductProfile extends RealmObject {
private String profileTitle;
private RealmList< ProductSelection > productSelection;
…
}
I then created a couple of ProductSelection Objects and ProductProfile Objects.
realm.beginTransaction();
ProductSelection prodSelection = realm.createObject(ProductSelection.class);
prodSelection.setSelectedProductName(prodTv.getText().toString());
prodSelection.setSelectedProductID(prodIdTv.getText().toString());
…
realm.commitTransaction();
…
realm.beginTransaction();
ProductProfile profile = realm.createObject(ProductProfile.class);
profile.setProfileTitle(“Some Title”);
RealmResults< ProductSelection > results =
realm.allObjects(ProductSelection.class);
RealmList< ProductSelection > selectionList = new RealmList<>();
for (ProductSelection selection : results) {
selectionList.add(selection);
}
profile.setProductSelection(selectionList);
realm.commitTransaction();
Now I have the following questions:
When I call
realm.allObjects(ProductSelection.class).clear();
it seems not only my ProductSelection Objects are cleared but also the List of ProductSelection Objects that are part of ProductProfile Class, as they now don’t hold any Objects anymore. Is it possible to prevent this, that is removing all ProductSelection Objects but still keeping the Objects that are part ProductProfile’s list?
Is the other way around also possible, that is removing ProductProfile Objects without removing ProductSelection Objects, that are not part of the productSelection List?
No, there isn't. Realm is a typed database, so you can think of a RealmObject class a SQLite table. Clearing that, will remove all data, even if it is referenced from other classes.
The solution would be to make a query for the ProductSelection objects and delete those explicitly:
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.where(ProductSelection.class).equalTo("someField", "someValue")
.findAll()
.deleteAllFromRealm();
// deleteAllFromRealm() since 0.89.0, in previous versions it's `clear()`
}
});
I actually can't think of a simple way of supporting this explicit use-case, unless you use bi-directional mapping (if your ProductProfile class contains a ProductSelection, then that ProductSelection also contains its ProductProfile).
Because then you can do this
realm.where(ProductSelection.class).isEmpty("productProfileList").findAll().deleteAllFromRealm();
// clear before 0.89.0, deleteAllFromRealm() after 0.89.0+
You have to manage this mapping yourself, though.
Related
I am applying filters on realm using RealmResults<>.
I begin to do like this -
RealmResults<data> filteredRealmResults;
List<data> tranfilteredlist;
private OrderedRealmCollectionChangeListener<RealmResults<data>> filteredTransChangeListener =
new OrderedRealmCollectionChangeListener<RealmResults<data>>() {
#Override
public void onChange(RealmResults<data> results, OrderedCollectionChangeSet changeSet) {
Log.d("realm", "filteredRealmResults.size():" + filteredRealmResults.size());
tranfilteredlist = results;
initFilterAdapter();
}
};
Now I want to delete the filteredRealmResults. I did like this -
void deleteFilteredRealmResults() {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Delete all matches
filteredRealmResults.deleteAllFromRealm();
}
});
}
After doing this my data in the realm got deleted. So I just try to delete the tranfilteredlist but it throws an exception that it does not support .clear();
I want to clear if from the memory whatever is holder the query data. Correct me if I am wrong or doesn't understand or just worrying too much.
I read This class holds all the matches of a RealmQuery for a given Realm. The objects are not copied from the Realm to the RealmResults list, but are just referenced from the RealmResult instead. This saves memory and increases speed.
I want to clear if from the memory whatever is holder the query data.
Correct me if I am wrong or doesn't understand or just worrying too
much.
Once you invoke filteredRealmResults.deleteAllFromRealm, it will clear the internal resultant elements object(which holds the elements) and as you know, resultant objects are reference so data will be deleted from realm database too. Hence, there is no need to call clear on the RealmResults object.
You can verify this by calling filteredRealmResults.size() after deletion, it will return 0.
I just try to delete the tranfilteredlist but it throws an exception
that it does not support .clear();
It is the expected behaviour as clear has been deprecated so don't use it.
Why deprecated?
deleteAllFromRealm automatically clears the list so no need to call it again explicitly.
Calling clear on RealmResults object will result in deletion of data from database, can cause unexpected behaviour if the user is not aware so API is being modified to avoid unexpected behaviours.
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.
I am trying to query my Realm DB such that the output will give an unmanaged object and for that, I changed my RealmList type of object to List.
Now the thing is in addchangeListener I am getting my output object(stories) value as managed. But the type of stories is List. So why my stories object is becoming managed where it should act as an unmanaged object.
List<story> stories = realm.where(story.class).findAllAsync();
stories.addChangeListener(new RealmChangeListener<RealmResults<story>>() {
#Override
public void onChange(RealmResults<story> storydata) {
if (storydata.size() != 0) {
madapter = new StoriesAdapter(stories, getBaseContext(), MR);
mrecyclerview.setNestedScrollingEnabled(false);
mrecyclerview.setLayoutManager(new LinearLayoutManager(getBaseContext()));
mrecyclerview.setAdapter(madapter);
}
}
});
StoriesAdapter
class StoriesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<story> storyList;
StoriesAdapter(List<story> storyList) {
this.storyList = storyList;
}
}
I am saying my List is managed because when i am trying to write below code I am getting Cannot modify managed objects outside of a write transaction.
madapter.storyList.get(3).setTitle("Wonderland"); // where storyList is List which i am pointing to `stories`.
List<story> stories = realm.where(story.class).findAllAsync();
Because specifying the type List<story> just means you'll see the returned list as a List<story>, but technically it's still a RealmResults<story>.
stories.addChangeListener(new RealmChangeListener<RealmResults<story>>() {
This line underneath shouldn't even compile.
Stories should be stored in a field.
private RealmResults<story> stories;
public void ...() {
stories = ...
stories.addChangeListener(...
Anyways, so you are working with RealmResults, which means that in
class StoriesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<story> storyList;
This storyList you provided is a RealmResults<story>, so calling storyList.get(...) will return managed RealmObjects.
Managed RealmObjects are "temporarily immutable", meaning they can only be modified in a transaction. It is also generally not recommended to run write transactions on the UI thread.
The simplest way would be to use realm-android-adapters.
class StoriesAdapter extends RealmRecyclerViewAdapter<story, RecyclerView.ViewHolder> {
StoriesAdapter(OrderedRealmCollection<story> stories) {
super(stories, true, true);
}
}
And when you want to modify an object, you do
story item = getData().get(3);
final String id = item.getId();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
story changedItem = realm.where(story.class).equalTo("id", id).findFirst();
changedItem.setTitle("Wonderland");
}
});
And then Realm will handle automatically updating the RealmResults, the story object, and the RecyclerView.
EDIT: If you intend to use unmanaged objects, then you could use realm.copyFromRealm(results), except that does the read on the UI thread.
You could create a background looper thread and obtain the results from there, but managing that could be tricky. Luckily for you, there's a library I made called Monarchy which lets you do exactly that.
See the relevant sample code for how you'd use it.
The stories is implicitly Managed, the reason is that RealmResults extends the list interface abstractly. Thats why the casting is possible, underneath the same mechanisms for a RealmResults still takes precedence. Also, you should only pass RealmResults instance to an Adapter directly, if you register a RealmChangeListener on it, which will call adapter.notifyDataSetChanged(). Otherwise, writes will update the RealmResults content, and your adapter will be desynchronized.
Realm is not like SQLite or Core Data. If you’re using Realm, take advantage of live objects. Don’t implement any refreshing logic or requerying. Always allow the current class to own its own instance of a realm query.
This fact is true,Realm objects and any child objects are NOT thread-safe. They’re confined to a single thread to ensure that atomic rights are maintained. There is an internal list where every single thread has its own unique Realm instance. If you want to pass objects between a thread–for example, if you create a dog object on the main thread, pass it to the background thread, and then try and access a property–it will trigger an exception straight away.
Also you are using asynchronous query, which puts it on a worker thread.
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();
I am having trouble using a query in realm.io. My code:
public static void delete(Context context, Workday workday) {
Realm realm = getRealm(context);
realm.beginTransaction();
RealmResults<Workday> workdays = realm.where(Workday.class)
.equalTo("date", workday.getDate())
.equalTo("hours", workday.getHours())
.equalTo("minutes", workday.getMinutes())
.findAll();
workdays.remove(0);
realm.commitTransaction();
}
Debug:
Why is the data visible in the JSON line but not in the fields itself? What am I doing wrong?
Thanks in advance!
UPDATE:
This is my delete function and it does find the workday1 object:
public static void delete(Context context, Workday workday) {
Realm realm = getRealm(context);
realm.beginTransaction();
Workday workday1 = realm.where(Workday.class)
.equalTo("date", workday.getDate())
.equalTo("hours", workday.getHours())
.equalTo("minutes", workday.getMinutes())
.findFirst();
workday1.removeFromRealm();
realm.commitTransaction();
}
When it executes the removeFromRealm method it crashes:
java.lang.IllegalStateException: Illegal State: Row/Object is no longer valid to operate on. Was it deleted?
How can I fix this?
You're doing nothing wrong! :)
Realm proxies your objects so that there won't be copies of your data all over the place. The getters and setters are overridden by the proxy classes and access your data directly in Realm. Of course this makes it harder to inspect the objects during debug, as you could notice, but that's why we've also overridden toString() to show something meaningful.