Items not getting deleted from realm - android

io.realm:realm-gradle-plugin:2.0.0'
Android Studio 2.2.2
I am trying to delete objects from the realm database. The items seems to get deleted. But when I close the app and load items from the database the deleted ones still seem to have a reference to them. This is my code below for deleting.
If the delete onSuccess is called I send back the item to be removed from the recyclerview's adapter. Is this the correct way to do this?
#Override
public void deletePerson(final Person person, final DeleteListener deleteListener) {
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<Person> results = realm.where(Person.class).equalTo("mId", person.getId()).findAll();
results.deleteAllFromRealm();
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
/* send the person object back to be removed from the recyclerview after success*/
deleteListener.onDeleteSuccess(person);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
deleteListener.onDeleteFailure(error.getMessage());
}
});
}
And when I load the persons the ones that are deleted seem to have a reference in realm and doesn't seem to be completely removed.
#Override
public void loadPersons(final LoadPersonListener loadPersonListener) {
if(mRealm.isClosed()) {
mRealm = Realm.getDefaultInstance();
}
RealmResults<Person> personsList = mRealm.where(Person.class).findAll();
if(personsList.size() > 0) {
loadPersonListener.onLoadPersonSuccess(personsList);
}
else {
loadPersonListener.onLoadPersonFailure("No items in the database");
}
}

You aren't removing anything from the Realm at the moment, you're just querying. Also, you're accessing the Person you sent in on a background thread, which ought to throw IllegalStateException.
So instead of
#Override
public void deletePerson(final Person person, final DeleteListener deleteListener) {
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<Person> results = realm.where(Person.class).equalTo("mId", person.getId()).findAll();
}
You should have
#Override
public void deletePerson(final Person person, final DeleteListener deleteListener) {
final String id = person.getId();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.where(Person.class).equalTo("mId", id).findAll().deleteAllFromRealm();
}

Here in you code you only query data asynchronously. For deletion use .remove() method while looping on result of query.
Suppose, that mId is #Primary key, removal will look like:
RealmResults<Person> results = realm.where(Person.class).equalTo("mId", person.getId()).findFirst().removeFromRealm();

Related

How to return value when Realm transaction success?

I have this method, i want return value when the transaction complete, but i cant. This's my code
public List<Group> getConversations() {
final RealmResults<Group> conversations;
try {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<Group> conversations = realm.where(Group.class).findAllSorted("time", Sort.DESCENDING);
cursorConversation(conversations);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
//return conversation
}
});
}
return null;
}
What should i do ?
I am not sure what are you doing in cursorConversation(..) but you can use the same method on returned values from Realm.
give a try
public List<Group> getConversations() {
try (Realm realm = Realm.getDefaultInstance()) {
return realm.copyFromRealm(realm.where(Group.class).findAllSorted("time", Sort.DESCENDING));
}
}
You don't need to run a transaction for getting the conversations. You can run your query on the realm db and add a change listener to the result. When the query completes, it'll call that change listener with the RealmResults<Converstaion>, Check this link for more.
Something like
public void listenToConversations(RealmChangeListener<RealmResults<Conversation>> listener) {
RealmResults<Conversations> conversations = realm.where(Group.class).sort("time", Sort.DESCENDING).findAllAsync();
conversations.addChangeListener(listener);
}
where listener is something like
listener = new RealmChangeListener<RealmResults<Conversations>>() {
\#Override
public void onChange(RealmResults<Conversations> conversations) {
// React to change
}
}
You'll also need to remove listener to avoid any memory leaks.

Android Realm - RealmObject returning after deletion

I'm running in to an issue where I delete a RealmObject (and confirm its gone using StethoRealm), but when I create and save a new RealmObject with the same variable value, the deleted RealmObject reappears along with my new RealmObject.
With the below object - I delete the myObject1 by matching the 'id', but when I create a new MyObject myObject2 with the same 'name' both objects appear in my realm
Any tips on why this might be happening?
Example:
public class MyObject extends RealmObject {
#PrimaryKey
private String id;
private String name;
// getters and setters
}
Edit
Delete call
#Override
public void deleteMyObjectById(final String id, final OnDeleteMyObjectById callback) {
Realm realm = new MyRealmConfiguration().getDefaultRealm();
final RealmResults<MyObject> myObjects = realm.where(MyObject.class).equalTo(RealmTable.MyObject.ID, id).findAll();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
myObjects.deleteAllFromRealm();
if (callback != null) {
callback.onSuccess();
}
}
});
realm.close();
}
Add call
#Override
public void addMyObject(MyObject myObject, OnSaveMyObjectCallback callback) {
Realm realm = new MyRealmConfiguration().getDefaultRealm();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(myObject);
if (callback != null) {
callback.onSuccess();
}
}
});
realm.close();
}
I'm not sure as to how you are deleting your objects. The way I'm deleting them (and it works) is...
final RealmResults<UserCredentials> credentialResults = realm.where(UserCredentials.class).findAll();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
credentialResults.deleteAllFromRealm();
}
});
realm.close();
Of course, you are using MyObject instead of UserCredentials, and you aren't deleting everything of that type, but it should be similar enough. Try using executeTransaction and realm.close() if you aren't already.
Try something like this. get the instance inside transcation method and delete. i have been using this for one of my app. and it works fine with deletion process
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<MyObject> myObjects = realm.where(MyObject.class).equalTo(RealmTable.MyObject.ID, id).findAll();
myObjects.deleteAllFromRealm();
}
});

How works delete in realm with relationship?

I have this classes
class Student extends RealmObject {
public String code;
public String name;
public String email;
public Course course;
}
class Course extends RealmObject {
public String code;
public String name;
}
class Sync {
// ...
// To sync data I am using retrofit, look the method to update course
public void onResponse(Call<...> call, Response<...> response) {
if (response.isSuccessful()) {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.delete(Course.class);
realm.copyToRealm(response.body());
}
});
}
}
}
After call Sync to update Courses, all Student object has its course setting to null, this is expected behavior after called realm delete?
Even after table is populated again, the course on Student is still null.
Today I made this change on the code:
class Course extends RealmObject {
#PrimaryKey
public String code;
public String name;
}
class Sync {
// ...
// To sync data I am using retrofit, look the method to update course
public void onResponse(Call<...> call, Response<...> response) {
if (response.isSuccessful()) {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(response.body());
}
});
}
}
}
I made this too late to avoid delete the courses.
There is something that can I do to recovery the references courses and set it again to student?
Thank you.
This is expected behavior, because you invalidate the object links by deleting the objects you are pointing to.
To restore them, you would have to set the links again.
Another solution would be to not delete courses that you still need. This would be done if you annotate code with #PrimaryKey, that way you would "update" courses that are already in. Then the problem would be removing courses/students no longer in the response, but there are solutions ready-made for that.
public class Robject extends RealmObject {
#PrimaryKey
private String code;
#Index
private String name;
//...
#Index
private boolean isBeingSaved;
//getters, setters
}
And
// background thread
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Robject robject = new Robject();
for(Some some : somethings) {
robject.set(some....);
realm.insertOrUpdate(robject);
}
realm.where(Robject.class)
.equalTo(Robject.IS_BEING_SAVED, false) // compile 'dk.ilios:realmfieldnameshelper:1.1.0'
.findAll()
.deleteAllFromRealm(); // delete all non-saved data
for(Robject robject : realm.where(Robject.class).findAll()) { // realm 0.89.0+
robject.setIsBeingSaved(false); // reset all save state
}
}
});
} finally {
if(realm != null) {
realm.close();
}
}

Your Realm is opened from a thread without a Looper

I thought I was following the recommended Realm approach for running Async data inserts like this:
public void addCustomer(final Customer customer) {
Realm insertRealm = Realm.getDefaultInstance();
insertRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm backgroundRealm) {
long id = customerPrimaryKey.incrementAndGet();
customer.setId(id);
backgroundRealm.copyToRealm(customer);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d(LOG_TAG, "Customer Added");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.d(LOG_TAG, error.getMessage());
}
});
insertRealm.close();
}
However, when I run the above code I get "Your Realm is opened from a thread without a Looper and you provided a callback, we need a Handler to invoke your callback"
I am running this code in a non-Activity class, what I am doing wrong here and how can I fix it. Thanks.
Update - Fixed
It turns out that there is nothing wrong with the query, problem is that I was calling it from IntentService. I was trying to seed the database on app first run, so I fixed this like this:
protected void onHandleIntent(Intent intent) {
Realm realm = Realm.getDefaultInstance();
//Add sample Customers to database
List<Customer> customers = SampleCustomerData.getCustomers();
realm.beginTransaction();
for (Customer customer: customers){
customer.setId(customerPrimaryKey.getAndIncrement());
realm.copyToRealm(customer);
}
realm.commitTransaction();
realm.close();
}
That fixed, outside of the IntentService, the query works fine when called from a UI Thread.
In order for
insertRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm backgroundRealm) {
//...
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// !!!
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// !!!
}
});
asynchronous transaction callbacks to be called on a background thread, the thread needs to be associated with a Looper (and therefore have Handlers that can communicate with the thread with that Looper).
Solution, use synchronous transaction on background thread.
But you've already figured that out.
protected void onHandleIntent(Intent intent) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
final List<Customer> customers = SampleCustomerData.getCustomers();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
for (Customer customer: customers){
customer.setId(customerPrimaryKey.getAndIncrement());
realm.insert(customer);
}
}
});
} finally {
if(realm != null) {
realm.close();
}
}
}
Realm relies on Android' thread communication by Hanfler and Looper classes.
It looks like you query async operation from another background Thread (why?, it is already in background, use synchronous version).
To fix that you need thread with Active Looper. Use Handler thread as you background thread - it will have Looper initialized
You need to call addCustomer from UI thread.
Try this line
insertRealm.close();
to add within onSuccess and onError method. Remove it from last line as it is currently.
So your code will look like
public void addCustomer(final Customer customer) {
final Realm insertRealm = Realm.getDefaultInstance();
insertRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm backgroundRealm) {
long id = customerPrimaryKey.incrementAndGet();
customer.setId(id);
backgroundRealm.copyToRealm(customer);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d(LOG_TAG, "Customer Added");
insertRealm.close();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.d(LOG_TAG, error.getMessage());
insertRealm.close();
}
});
}

Get All in Realm returns deleted Items

I implementing a recycler view that gets its data set from a realm database using rx-java. When i delete items from the data set then call get all items, i receive the remaining items plus the items i just deleted but invalidated.
Observable.defer(list -> {
boolean isDeleted = true;
for (int i = 0; i < list.size(); i++)
isDeleted = isDeleted && evictById(list.get(i));
return Observable.just(isDeleted);
}).subscribe(new Subscriber{
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
#Override
public void onNext(Boolean success) {
if (success)
getUserList();
}
});
boolean evictById(final int itemId){
mRealm = Realm.getDefaultInstance();
RealmModel toDelete = mRealm.where(UserRealmModel.class).equalTo("userId", itemId).findFirst();
if (toDelete != null) {
mRealm.beginTransaction();
RealmObject.deleteFromRealm(toDelete);
mRealm.commitTransaction();
return !RealmObject.isValid(toDelete);
} else return false;
}
public void getUserList(){
Observable.from(Realm.getDefaultInstance().where(UserRealmModel.class).findAll())
.subscribe(new Subscriber<List<UserRealmModel>> {
#Override
public void onCompleted() {
hideViewLoading();
}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(List<UserViewModel> users) {
showUsersCollectionInView(users);
});
}
For example the data set has 10 items, i delete 3 of them. In the getUserList response i receive a list of 10 items with 7 valid items and 3 invalid.
Also i made sure, using a realm browser, that the items deleted!
Can someone please explain why is this happening ?
It sounds like you are indirectly deleting items that are part of query result, and it is correct that is part of the breaking changes of 0.89. Instead you should remove them directly from your query result.
public boolean evictById(UserRealmModel obj) {
obj.deleteFromRealm()
return true;
}
// should be
public boolean evictById(RealmResults list, int index) {
list.deleteFromRealm(index);
return true;
}
You didn't provide the implementation of evictById so I'm guessing your method does more as well, but hopefully you get the idea.
Also any query run after you deleted some objects should never contain deleted objects.

Categories

Resources