Firebase with Room persistance library - java

I was thinking about fetching data from Firebase and storing it on my phone using room library. But, I have a doubt in my mind. I want to use Firebase ChildEventListener because I want to fetch data only when a child is changed or added. But, I also know that ChildEventListener fetches all data when the app is started for the first time. So,my question is that in what cases firebase ChildEventListener will fetch whole data and in what cases a single child?. Actually, knowing this is important because if ChildEventListener fetches same data twice, room will give unique id duplication error.

If you cannot control when ChildEventListener is firing, an easy strategy would be to overwrite in all cases. You can avoid unique id duplication error with following code:
#Insert(onConflict = OnConflictStrategy.REPLACE)
If replace is not suitable for you there are other options.

Classes implementing ChildEventListener interface can be used to receive events about changes in the child locations of a given database reference. You should use this interface only if you want to respond to child-level changes because this interface has separate methods for when a child is added, removed, changed or moved (onChildAdded(), onChildRemoved(), onChildChanged() and onChildMoved() respectively).
In what cases firebase ChildEventListener will fetch whole data and in what cases a single child?
For example, onChildAdded() method is called once when you start the app to get all the children from a particular location but is also called everytime a new child is added at that location.

You can use below code to remove duplicate of data
#Entity(tableName = "post",indices = #Index(value = {"id"},unique = true))
In your model class you can give your "id" as unique so that same data will not store multiple times. Remember one point don't provide your primary key as unique you have to provide a separate id for your Room Database. hope it will help you :)

Related

Android Firebase how to handle real time server to local database connection

As for similar questions on this topic and on ChildEventListener, there is no relevant answer, so heres mine.
I have a local SQLite DB which holds all the data, I also have Firebase realtime database which I'm updating with new entries or real time changes across all users. I'm currently doing it with the use of ChildEventListener as follows:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getDatabase().getReference();
DatabaseReference childRef = rootRef.child("my_root");
ChildEventListener eventListener = new ChildEventListener()
{
....
};
childRef.addChildEventListener(eventListener);
As for functionality, With this code I can get realtime changes on childs, get new entries, deleted childs and everything I need but there is one problem. When this specific activity with the listener loads up, the onChildAdded listener gets called enormous amounts of times for every child on this root, as stated on the documentation:
child_added is triggered once for each existing child and then again every time a new child is added to the specified path
So I though to gain focus on the items that I really need and I have done it with:
rootRef.orderByKey().startAt("-WhatTF123456789")...
But then I have lost my CRUD capabilities because it's listening to the new entries and not all of them.
So I came up with a solution. Keep node with all the changes that has been made to the FireBase database and a node with all the users that have read and made the changes to the local DB to know who needs an update, Then use addChildEventListener to this specific node. But that seems redundant.
What is my options to handle this kind of situation?
The onChildAdded listener gets called enormous amounts of times for every child on this root.
As you already mentioned and as the docs states, this is the expected behaviour. Usually, is not recommended to attach a ChildEventListener on a node (root node) that contains huge amount of data. Please be careful about this practice because when downloading large amount of data, you can get erros like: OutOfMemoryError. This is happening because you implicitly download the entire node that you are listening to, along with all the data beneath it. That data might be present as simple properties or, as complex objects. So it can be considered a waste of resource and bandwidth. In this case, the best approach is to flatten the database as much as possible. If you are new to NoSQL databases, this practice is called denormalization and is a common practice when it comes to Firebase. For a better understanding, I recommend you take a look at:
This video, Denormalization is normal with the Firebase Database.
Official docs regarding Best practices for data structure in Firebase realtime database.
My answer from this post: What is denormalization in Firebase Cloud Firestore?
This article, Structuring your Firebase Data correctly for a Complex App.
This article, NoSQL data modeling techniques.
Please also note that when you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
I also recommend you to see the last part of my answer from the following post:
What is the correct way to structure this kind of data in firestore?
It is for Cloud Firestore but same rules apply to Firebase realtime database.
But then I have lost my CRUD capabilities because it's listening to the new entries and not all of them.
Everything in Firebase is about listeners. You cannot get realtime updates for objects within a node, unless you are listening to them. So you cannot limit the results and expect to get updates from objects that you are not listening to. If you need to get updates for all objects within a node, you need to listen to all of them. Because this approach isn't practical at all, you can either use denormalization as explained above or to restrict the results by using queries that can help you limit the amount of data that you get from the database. Regarding your solutions, the second one is much preferred but you can also consider another approach which would be to load data in smaller chunks according to a timestamp property, or according to any other property that you need.
Edit: According to your comment:
Can you please provide tests for each solution (1.denormalization, 2.my solution) examine use of bandwidth and resources and which one is really preferred?
All data is modeled to allow the use-cases that an app requires. Unfortunately, I cannot do tests because it really depends on the use-case of the app and the amount of data that it contains. This means that what works for one app, may be insufficient for another app. So the tests might not be correct for everyone. The denormalization process or your solution is entirely dependent on how you intend to query the database. In the list above, I have added a new resource which is an answer of mine regarding the denormalization tehnique in NoSQL databases. Hope it will also help feature visitors.
I would make a root node with the name, for example, MaintenanceUpdate.
All clients are subscribed to changes here.
As soon as MaintenanceUpdate becomes = true, all clients unsubscribe from changes to the main "database". And then (when MaintenanceUpdate = false) are re-subscribed again.
At this time you are updating the database.
I have similar requirements, with Firebase and Room, while I've solved it alike this:
public class BaseModel extends BaseObservable implements IDataModel {
/** Sqlite default PK */
private int itemId = 0;
/** Firebase uniqueId */
#ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_UNIQUE_ID)
protected String uniqueId = null;
/** Firebase lastSync */
#ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_LAST_SYNC)
protected long lastSync = 0;
...
}
this means, when a local record has a KEY_FIREBASE_UNIQUE_ID which is null and the KEY_FIREBASE_LAST_SYNC is 0, it has to be inserted into Firebase - else it would check, when running a synchronization AsyncTask, if the local or remote record needs to be updated. this is because the main issue is, that when inserting remotely, the ChildEventListener will attempt to synchronize duplicates to the same client - unless having such indicators for the synchronization status in place, locally and remotely. the local primary keys might vary across the clients (depending for how long they were offline and how many records where locally inserted during the offline state), while the synthetic KEY_FIREBASE_UNIQUE_ID is used for identifying them; it's the "key to success".

Update field in Firebase with condition

I have this structure of Firebase, and I am trying to update the Author in all post-comments when the user updates his nick_name.
I used this query in Android but it didn't fetch all the data that I need:
mDatabase = FirebaseDatabase.getInstance().getReference("DataBase").child("post-comments");
Query query = mDatabase.orderByChild("uid");
and down here you will find a picture of the Firebase structure:
As I understand from your comments, you trying to change the named of this node -KykDRTQ5GToIe6oh_kA to commentID. The short answer is no, there is no way in which you can change the names of the nodes from your Firebase database. There is no API for doing that. What can you do instead is to attach a listener on that particular node and get the DataSnapshot object. Having that data, you can write it in another place using another name. In the end just remove the old node. So you cannot simply rename -KykDRTQ5GToIe6oh_kA to commentID.

How can I keep track which person has added which data to a database?

I have an app where anyone who downloads the app can add data to the database. The data THEY added will get displayed in a listView for only them to see. The users don't have to register an account or anything. Now when multiple people use the app, different data gets added to the database. So my question is, what are good ways to differentiate data, so the person who added data, only sees the data they added.
I have two ideas, either add the data ID to sharedpreferences, so when I select the data from the database, I select data where the data ID equals to the one in sharedpreferences OR when I add the data to the database, I add a unique key, so I can select * where unique key equals x.
I like the second idea more, but what would be a good unique key to use. I've thought of using
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
but is that a reliable solution, are there any other unique key solutions I could use?
Any feedback is much appreciated!
I think that the best way to handle such cases is to generate a UUID for each user.
Android itself will help you generate one.
You can generate the UUID the first time the app is launched, and store id in SharedPreferences.

How to temporarily store parts of data?

I have parts of data the coming not by order. Let me explain :
User with id 1 sending data ("phone number") but in the same time user with id 78 sending another data ("home address").
All the incoming data have the same receiver.
When all set of the data per id is set the data would be sent to mysql database and deleted from temporarily storage.
Each user id need to fill 6 different information before the complete data is sent.
So the question is how to to store the temporary data parts using their personal id (without mixing them up) and only then I have all the parts I will proceed to the next task ?
Should I use arrayList or something different ?
Edit (answering the duplicate suggestion ): my question is a bit different and the answer there not helping me at all !
You could, as you say, store it in some structure in memory. I'd pick some method which isn't tied to an Activity, otherwise you need to track the activity lifecycle. E.g. you could have a HashMap to map entity name (i.e. name, address, etc) to its value (or in case you're storing multiple instances of a single entity, go with some kind of a Data class suggested in the other answer). Personally, I find this method most cumbersome/cluttered, because you need to either divorce your storage from your Activity or handle all activity changes in order not to lose state.
Second approach is using SharedPreferences. Make a separate prefs file for that purpose, obtain them (getSharedPreferences("partial_data_dl", MODE_PRIVATE)), store each column as one field in the prefs, and clear them before commiting them to the database. It's easier than storing everything in memory and shouldn't be noticeably slower.
Third is the obvious one: why don't you update the database record as the data comes in? First time insert a new record with only one column populated, every other issue an update query to add value for the new column. This is admittedly the heaviest solution and might not work for your use case, but I'd give it a try and test it out. As the saying goes, premature optimization is the root of all evil.
you can create object 'Data'
class Data {
private param1,param2,...,param6;
public Data(){
}
/* PARAMS getters */
/* PARAMS setters */
public void insert(){
//insert to database
}
}
then use HashMap to update the data :
HashMap<Integer, Data> map = new HashMap<Integer, Data>();
and every time you recive some data and id check the type of the data (which param in the Data object) and update it in the map
Data toUpdate = map.get(id);
toUpdate.setParam...
you can make a listener for each time you set some param to indicate that all the data had been set and ready to be inserted

Observing #Relation changes in a DataBase

If I something like the following:
#Relation(parentColumn = "id", entityColumn = "fk_id", entity = User.class)
private List<User> users;
This list is a model which gets updated via LiveData.
But I want to be able to update this when I detect changes to the User table. Right now, it only picks up what exists in the DB, but whenever an update happens to the user table, this list does not get updated. Any suggestions?
You have not been very specific in what you are doing or want (which database you use, who is updating the database...).
But I want to be able to update this when I detect changes to the User
table. Right now, this only picks up what exists in the DB but
whenever an update happens to the user table, this list does not get
updated. Any suggestions?
Option 1
If you are doing the update/insert/delete in the same app as the List users, then you can easily update your List in your code.
Option 2
If another app/service is updating the database, I suggest you use a trigger to update your List.
See http://www.coderconsole.com/2015/02/android-sqlite-trigger-demo.html for an example.
Detecting the change in Java is tricky, see Calling a Java method from a sqlite trigger (android) .
Here is an example where you use a ContentObserver to monitor your Sqlite database (the flat file itself) https://gist.github.com/JBirdVegas/3874450
Option 3
If another app/service is updating the database, you can use a BroadcastReceiver to pick up the changes.

Categories