I am using Realm to store my values in local database.
My requirement is that i need to change one field status=1 based on some condition.
I have tried following method to accomplish this task. And it is working fine.
RealmResults<NotificationOrder> notificationOrders=realm
.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll();
for (NotificationOrder order:notificationOrders) {
realm.beginTransaction();
order.setStatus(1);
realm.commitTransaction();
}
Now there may be 1000 of such rows in my local db and using for loop to update single row doesn't seem proper way.
So my question :Is there any way like MYSQL Update Queries in Realm by which we can update all rows having status=0 by single statement instead of updating single row one by one ?
Thanks.
If I know right, the objects in the transaction ought to be managed, so
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<NotificationOrder> notificationOrders = realm
.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll();
for(NotificationOrder order : notificationOrders) {
order.setStatus(1);
}
}
});
Should be sufficient.
You can do, bulk update by setValue method like this:
realm.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll()
.setValue(RealmConstants.REALM_FIELD_NAME,value); // 1st parameter is field name, 2nd is value
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.
Android studio 3.2
public class Profile extend RealmObject {
#PrimaryKey
private long id;
}
I has List<Profile> profileList;
I this list I has 5 profiles with id = 1, 2, ...
Nice.
Now I need to delete from Realm profiles with id=1 ,id=3, id=5
So after delete the list must content only 2 profiles with id=2 and id=4
How I can do this?
P.S. The list of ids is a dynamic list. Today it has 3 ids , but tomorrow can 2 ids.
Well that's actually quite simple, considering there's direct support for it...
r.executeTransaction((realm) -> {
realm.where(Profile.class).in("id", new Long[] { 1L, 3L, 5L }).findAll().deleteAllFromRealm();
});
See https://realm.io/docs/java/latest/api/io/realm/RealmQuery.html#in-java.lang.String-java.lang.Long:A-
As per the Realm documentation, you need to search for the all possible matches
// obtain the results of a query
final RealmResults<Profile> results = realm.where(Profile.class).equalTo("profile.id", 1).where().equalTo("profile.id", 3).where().equalTo("profile.id", 5).findAll();
// All changes to data must happen in a transaction
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// remove single match
results.deleteFirstFromRealm();
results.deleteLastFromRealm();
// remove a single object
Dog dog = results.get(5);
dog.deleteFromRealm();
// Delete all matches
results.deleteAllFromRealm();
}
});
Realm doc
I have 2 tables in database, Course and Lecture. They are 1:N relationship. My problem is I want to delete multiple courses, before that I have to make sure all its relative Lectures are deleted, as well as some files along with the lecture. That is, I want to delete multiple course, for every course, the following steps should be perform:
delete lecture file and record delete course
delete course
How to do it with RxJava 1.x? Thanks.
I think it would be like :
ArrayList<Course> courses = new ArrayList<>();
Observable.fromIterable(courses)
.doAfterNext(new Consumer<Course>() {
#Override
public void accept(Course course) throws Exception {
//DELETE this Course
}
}).flatMap(new Function<Course, ObservableSource<ArrayList<Lecture>>>() {
#Override
public ObservableSource<ArrayList<Lecture>> apply(Course course) throws Exception {
return Observable.fromArray(course.getAllLecture());
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ArrayList<Lecture>>() {
#Override
public void accept(ArrayList<Lecture> lectures) throws Exception {
//delete all lectures
}
});
If you're using GreenDao, I don't think that using RxJava in this way is your best bet. Your major problem here is that you're not in a transaction, which leaves you at risk of your data ending up in an inconsistent state. While it can be a great exercise to consider, "how can I write this code in Rx style?" I suggest that it doesn't gain you anything to use it for every step of this process. Therefore, I suggest you write your delete code as procedural (not Rx) code inside of a GreenDao transaction, and only use RxJava to be notified when it is completed. When you are inside of a GreenDao transaction block, all of the database calls inside of it are made synchronously, one after another, in guaranteed order.
In addition, for the greatest consistency, I would delete all of the files at once only after the transaction block is committed (because you may not want to delete the files if part of the DB transaction fails and the database doesn't update). In addition, there are two major ways of doing deletes in GreenDao: directly, session.delete(entity) and queries, query.buildDelete().tableDeleteQuery.executeDeleteWithoutDetachingEntities(). Direct deletes are much simpler to code, but could be slower if you have huge amounts of data. If you have less than 1000 very simple entities, direct deletes are probably good enough.
So, your code might look like this:
final DaoSession daoSession = getDaoSession();
final List<Course> courses = getCoursesToDelete();
// rxTx() creates a tx that runs on RxJava's 'io' scheduler.
daoSession.rxTx().call(() -> {
List<File> filesToDelete = new ArrayList<>();
for(Course course : courses) {
for(Lecture lecture : course.getLectures()) {
filesToDelete.add(lecture.getFiles());
daoSession.delete(lecture);
}
daoSession.delete(course);
}
return filesToDelete;
})
// potentially handle DB errors here
// .flatMapIterable here if you want each File as an Rx event
.doOnNext(filesToDelete -> {
for(File f : filesToDelete) {
// Throw on failed delete here if needed
f.delete();
}
})
// handle file delete errors if desired.
.subscribeOn(Schedulers.io()) // technically redundant
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
I insert records from a ContentObserver in an Android Service like:
try {
realm.beginTransaction();
realm.copyToRealm(sms);
realm.commitTransaction();
} catch (RealmException re) {
realm.cancelTransaction();
}
Then in another Service I run a JobService with the following query:
Realm realm = Realm.getDefaultInstance();
RealmQuery<Sms> query = realm.where(Sms.class);
RealmResults<Sms> sms = query.findAll();
ArrayList<Sms> smsArrayList = new ArrayList<>();
smsArrayList.addAll(sms);
for (Sms sms : smsArrayList) {
process row
delete row
}
Is the query read-consistent in point in time, like Oracle?
What happens if the query runs for some time and new data is inserted from the other Service?
I like my query to be read-consistent. The new records inserted after the query starts should not be seen or be included. Is this the case or how should I code it?
I need to be sure I process the records in the job queue properly and I want to avoid read the same job twice or mix up due to the other Service inserting.
So I'd like to do a for loop over RealmResults<< Sms>>, process it, delete the row.
Then at some point in time start the query again and process the new records.
On looper threads, Realms (and RealmResults and RealmObjects) are updated automatically. You can disable this by calling setAutoRefresh (https://realm.io/docs/java/latest/api/io/realm/Realm.html#setAutoRefresh-boolean-). Alternatively you can start a write transaction.
So you could do:
Realm realm = Realm.getDefaultInstance();
realm.setAutoRefresh(false);
// do all your reads here ...
realm.waitForChange(); // to update the instance if other threads have commit changes
or alternatively:
Realm realm = Realm.getDefaultInstance();
realm.writeTransaction(); // will wait if another write transaction is in progress
// do all your reads here ...
realm.cancelTransaction(); // no writes :-)
With the following code. I expect:
1. Get all the data from the server side.
2. Insert all the data to Realm
3. Refresh RecycleView with Realm Adapter.
The first time, the data is always empty. Maybe the data is still not ready but subscribe is still invoked? Is there any way to update the view when the data is ready?
ApiCall().doOnNext(response -> {
Realm realm = Realm.getDefaultInstance();
try {
realm.beginTransaction();
realm.insertOrUpdate(response);
realm.commitTransaction();
} finally {
realm.close();
}
}).subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new exObserver<List<T>>() {
#Override
public void onResponse(List<T> response) {
updateView()
}
#Override
public void onFailure(exException exception) {
adapter.notifyChanged();
}
});
As EpicPandaForce mentioned, you don't need to notify RealmRecyclerViewAdapter when there is a change.
My answer will specifically target your use of RxJava.
doOnNext() is a side-effect method.
This means it will be called parallel to your stream, without affecting it.
When your api call returns, the Action1 in doOnNext() and the Observer in subscribe() will both be triggered at the same time.
This means updateView() is called before the Realm transaction finishes.
If you want to update your view after inserting into the DB, your transaction must happen in your stream.
You could use flatMap() for this purpose.
RealmRecyclerViewAdapter already observes the provided data set with a RealmChangeListener, and calls adapter.notifyDataSetChanged() when a change happens in the underlying Realm.
Why are you trying to manually call adapter.notifyDataSetChanged()? The RealmRecyclerViewAdapter was specifically created so that you don't need to do that.
What is your apiCall() method? try using Observable.defer(()-> apicall()) if you're not using Retrofit. This will delay code evaluation until actual value arrive.