Cannot seem to write custom object to Firebase database? - java

I'm working on a project using Firebase, which I've never used before, and I know almost nothing about Firebase itself, as the rest of my team has been responsible for most of the dealings with it. I'm writing a parser for some Excel data where I need to extract some specific data and then upload it to Firebase. The parsing is done, but I'm having trouble writing it to Firebase.
We have a sub-database called "families" in our root database that I need to write this data to. I have a class called RegistrationSheet which contains all the data in this particular spreadsheet broken up into objects to represent the structure of the JSON. I'm aware that you can write custom objects to the Firebase database and it will be converted to a JSON format that represents that data. I found a page detailing the different data types that can be written to the database and converted to JSON, and among them were Map and List. So here are my classes that represent the "families" database.
RegistrationSheet.java:
public class RegistrationSheet {
public List<Object> families;
public void addFamily(Family f) { families.add(f); }
public RegistrationSheet() {
families = new ArrayList<>();
}
public void writeToFirebase() {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("families");
ref.removeValue(); // Delete the data currently in the database so we can rewrite it.
ref.setValue(families);
}
public File outputJSON() {
return null;
}
}
Family.java:
public class Family {
public Map<String, Object> fields;
public void addField(String str, Object obj) { fields.put(str, obj); }
public Family() {
fields = new HashMap<>();
}
}
Child.java:
public class Child {
public Map<String, Object> fields;
public void addField(String str, Object obj) { fields.put(str, obj); }
public Child() {
fields = new HashMap<>();
}
}
The families list contains Family objects, and one of the "fields" that can be added to the Map in the Family objects is a List of Child objects. I figured that because these are all objects that are valid to write to Firebase, that simply writing the "families" list in the RegistrationSheet object would be enough:
public void writeToFirebase() {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("families");
ref.removeValue(); // Delete the data currently in the database so we can rewrite it.
ref.setValue(families);
}
Is there something wrong with the structure of any of my classes or how I'm writing the data to Firebase? Because after executing this, the "families" sub-database disappears from Firebase and I have to restore it from my backup. It seems I have the correct DatabaseReference since removeValue() seems to be removing it, but why isn't it then writing the data from the families list?
I would appreciate any help that someone could provide.

Try the following code. It gives you the reason as to why it is not writing the value.
public void writeToFirebase() {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("families");
ref.removeValue(); // Delete the data currently in the database so we can rewrite it.
ref.setValue(object, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference reference) {
if (databaseError != null) {
Log.e(TAG, "Failed to write message", databaseError.toException());
}
}
});
}
Then you can debug your code based on the Exception generated

Related

Polymorphism in Firebase

I'm having a little trouble with Firebase. I've structured my data like this:
Class PoJo{
int field
String field
PolyPojo field
}
Class PolyPojo {
int typeForAll
}
Class PolyType1 {
int field1
String field2
}
Class PolyType2 {
boolean field3
long field4
}
I have the need for the PolyPojo to be instantiable and if nothing else happens in the code the default constructor for PoJo instantiates a PolyPojo to the PolyPojo field. The issue I am having is I am checking that a PolyType1 class is being instantiated and sent up to firebase. I check firebase and the data is stored correctly. When I try to read the data from my db ref like thus:
ref.child("users").child(user.getUid()).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (!task.isSuccessful()) {
Log.e("firebase", "Error getting data", task.getException());
}
else {
Log.d("firebase", String.valueOf(task.getResult().getValue()));
PoJo pojo = task.getResult().getValue(PoJo.class);
if (pojo != null) {
this.pojo = pojo;
onDataLoaded();
}
}
}
});
Everything on the parent class is fine and works correctly except the PolyPojo field, the issue I am having is that the PolyPojo field is being typed as just a PolyPojo and not the correct polymorphed class PolyType1.
Anyone know what I'm doing wrong?
For more context, all of the classes are correctly (AFAIK) implementing parcelable and serialization/deserialization from deconstruction and reconstruction of activities is working as expected, though I don't believe using the getValue(Class.class) works off those functions.
Firebase doesn't store any type information about the object you pass to it. If you expect to get a PolyPojo from the database, you'll need to explicitly say so in the call to getValue:
task.getResult().getValue(PolyPoJo.class)
Since Firebase doesn't store such information, this typically means that you also need to store the type information yourself - so add an additional property to the database that signals the data is for a PolyPojo object.

How to use non-keyed state with Kafka Consumer in Flink?

I'm trying to implement (just starting work with Java and Flink) a non-keyed state in KafkaConsumer object, since in this stage no keyBy() in called. This object is the front end and the first module to handle messages from Kafka.
SourceOutput is a proto file representing the message.
I have the KafkaConsumer object :
public class KafkaSourceFunction extends ProcessFunction<byte[], SourceOutput> implements Serializable
{
#Override
public void processElement(byte[] bytes, ProcessFunction<byte[], SourceOutput>.Context
context, Collector<SourceOutput> collector) throws Exception
{
// Here, I want to call to sorting method
collector.collect(output);
}
}
I have an object (KafkaSourceSort) that do all the sorting and should keep the unordered message in priorityQ in the state and also responsible to deliver the message if it comes in the right order thru the collector.
class SessionInfo
{
public PriorityQueue<SourceOutput> orderedMessages = null;
public void putMessage(SourceOutput Msg)
{
if(orderedMessages == null)
orderedMessages = new PriorityQueue<SourceOutput>(new SequenceComparator());
orderedMessages.add(Msg);
}
}
public class KafkaSourceState implements Serializable
{
public TreeMap<String, SessionInfo> Sessions = new TreeMap<>();
}
I read that I need to use a non-keyed state (ListState) which should contain a map of sessions while each session contains a priorityQ holding all messages related to this session.
I found an example so I implement this:
public class KafkaSourceSort implements SinkFunction<KafkaSourceSort>,
CheckpointedFunction
{
private transient ListState<KafkaSourceState> checkpointedState;
private KafkaSourceState state;
#Override
public void snapshotState(FunctionSnapshotContext functionSnapshotContext) throws Exception
{
checkpointedState.clear();
checkpointedState.add(state);
}
#Override
public void initializeState(FunctionInitializationContext context) throws Exception
{
ListStateDescriptor<KafkaSourceState> descriptor =
new ListStateDescriptor<KafkaSourceState>(
"KafkaSourceState",
TypeInformation.of(new TypeHint<KafkaSourceState>() {}));
checkpointedState = context.getOperatorStateStore().getListState(descriptor);
if (context.isRestored())
{
state = (KafkaSourceState) checkpointedState.get();
}
}
#Override
public void invoke(KafkaSourceState value, SinkFunction.Context contex) throws Exception
{
state = value;
// ...
}
}
I see that I need to implement an invoke message which probably will be called from processElement() but the signature of invoke() doesn't contain the collector and I don't understand how to do so or even if I did OK till now.
Please, a help will be appreciated.
Thanks.
A SinkFunction is a terminal node in the DAG that is your job graph. It doesn't have a Collector in its interface because it cannot emit anything downstream. It is expected to connect to an external service or data store and send data there.
If you share more about what you are trying to accomplish perhaps we can offer more assistance. There may be an easier way to go about this.

How to synchronously query data from room database?

This is my first time doing android dev. Sorry for my lack of knowledge in..well everything.
I am trying to query some data on the main thread of an activity using asynctask. The problem is, the data that I queried is needed immediately in some other query of data, so the asynchronous nature of the query means that every time I need to use the data, the thread has not queried it yet and gives a nullpointer exception.
Is there a way to synchronously query data from room database?
I tried the getValue() function from a LiveData object, but it always returns null as well. I am sure that the data is inserted properly within the database, I have checked multiple times looking into the database while debugging.
This is the code I used to query an entity of Day class:
//load current day
findSpecificDayAsyncTask asyncTask = (findSpecificDayAsyncTask) new findSpecificDayAsyncTask(mDayDao, new findSpecificDayAsyncTask.AsyncResponse() {
#Override
public void processFinish(Day output) {
day1 = output;
}
}).execute(date);
It works in due time, but I need the data immediately so that I can query
some other data:
mBPViewModel = ViewModelProviders.of(this).get(BulletPointViewModel.class);
//the day1 class is used here as a parameter
mBPViewModel.getSpecificDayBulletPoints(day1.day).observe(this, new Observer<List<BulletPoint>>() {
#Override
public void onChanged(#Nullable final List<BulletPoint> bulletPoints) {
// Update the cached copy of the words in the adapter.
mAdapter.setBulletPoints(bulletPoints);
}
});
So is there a way for me to synchronously query data so I don't get
a nullpointer exception?
Why not doing like this
//load current day
findSpecificDayAsyncTask asyncTask = (findSpecificDayAsyncTask) new
findSpecificDayAsyncTask(mDayDao, new findSpecificDayAsyncTask.AsyncResponse() {
#Override
public void processFinish(Day output) {
day1 = output;
mBPViewModel = ViewModelProviders.of(this).get(BulletPointViewModel.class);
//the day1 class is used here as a parameter
mBPViewModel.getSpecificDayBulletPoints(day1.day).observe(this, new Observer<List<BulletPoint>>() {
#Override
public void onChanged(#Nullable final List<BulletPoint> bulletPoints) {
// Update the cached copy of the words in the adapter.
mAdapter.setBulletPoints(bulletPoints);
}
});
}
}).execute(date);

How come I can't have several hashmaps as member variable in my Custom object

I'd like to have a firestore Document with several hashmaps as data objects on the server-side.
On the client-side (Android) I am using a POJO as a custom object to store and organize all data requested from firestore. This POJO implements Parcelable to be able to pass it through intent from an activity to another.
My POJO looks like this:
public class Session implements Parcelable {
private Map<String, Boolean> firstHashmap;
private Map<String, Boolean> secondHashmap;
private Map<String, String> thirdHashmap;
// constructors (first I notice that I can only create 1 constructor for a hashmap as a parameter, I wonder why)
// getters and setters
#Override
public int describeContents() {
return 0;
}
// Write the object's data to the passed-in Parcel
#Override
public void writeToParcel(Parcel out, int flags) {
if (firstHashmap != null) {
Bundle firstBundle = new Bundle();
for (Map.Entry<String, Boolean> entry : firstHashmap.entrySet()) {
firstBundle.putBoolean(entry.getKey(), entry.getValue());
}
out.writeBundle(firstBundle);
}
// same thing for secondHashmap
// same thing for thirdHashmap
public static final Parcelable.Creator<Session> CREATOR = new
Parcelable.Creator<Session>() {
public Session createFromParcel(Parcel in){
return new Session(in);
}
#Override
public Session[] newArray(int size) {
return new Session[size];
}
};
// Constructor that takes a Parcel and gives you an object populated with it's values
private Session(Parcel in) {
Bundle firstBundle = new Bundle();
firstBundle = in.readBundle(getClass().getClassLoader());
Map<String, Boolean> tempFirstHashmap = new HashMap<>();
for (String key: firstBundle.keySet()){
tempFirstHashmap.put(key, firstBundle.getBoolean(key));
}
firstHashmap = firstBundle;
// same thing for secondHashmap
// same thing for thirdHashmap
}
}
It worked fine while I was having the same value data type in my hashmap i.e. Boolean but then I introduced thirdHashmap with String and everything went wrong: for instance when firstHashmap is null on the firestore document and thirdHashmap is not, I'm having Strings deserialized to my firstHashmap causing RuntimeError.
To sum up I am having some issue to get data from firestore and put them in the right place. If anyone could give me a clue it would be much appreciated :)
Thank you very much!
To save you a lot of time and effort you can use a plugin to generate parceling code for you inside your POJO if you're using android studio just go to File>settings>plugins and there search for plugins and install it.
then go to your POJO and clear it from everything except your member variable(HashMap variables) after that just by right clicking your mouse on inside your class file and from your opened window click on generate you will find a new tab added to the last of the window called Parcelable click on it and choose what member variables you want to parcel.
Note: If i were in your shoes i would delete all my firestore data on server-side and on client-side when a new change made to my POJO specially when I delete or change member variable, I hope this answer help you.

What is the difference between insert () and createObject()?

I have a setChatsList() method and it has a huge code:
public void setChatsList(final ChatsModel chatsModel) {
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
ChatsModel realmChats = realm.createObject(ChatsModel.class);
Response realmResponse = realm.createObject(Response.class);
Item realmItem = realm.createObject(Item.class);
Message realmMessage = realm.createObject(Message.class);
Attachment realmAttachment = realm.createObject(Attachment.class);
Video realmVideo = realm.createObject(Video.class);
Response response = chatsModel.getResponse();
RealmList<Item> items = new RealmList<>();
Integer itemCount = response.getCount();
RealmList<Item> itemList = response.getItems();
if (itemList != null) {
for (Item item : itemList) {
Message message = item.getMessage();
realmMessage.setId(message.getId());
realmMessage.setDate(message.getDate());
realmMessage.setOut(message.getOut());
realmMessage.setUserId(message.getUserId());
realmMessage.setReadState(message.getReadState());
realmMessage.setTitle(message.getTitle());
realmMessage.setBody(message.getBody());
realmMessage.setRandomId(message.getRandomId());
RealmList<Attachment> attachments = message.getAttachments();
RealmList<Attachment> attachmentList = new RealmList<>();
if (attachments != null) {
for (Attachment attachment : attachments) {
String type = attachment.getType();
Video video = attachment.getVideo();
realmVideo.setAccessKey(video.getAccessKey());
realmVideo.setCanAdd(video.getCanAdd());
realmVideo.setCanEdit(video.getCanEdit());
realmVideo.setComments(video.getComments());
realmVideo.setDate(video.getDate());
realmVideo.setDescription(video.getDescription());
realmVideo.setDuration(video.getDuration());
realmVideo.setId(video.getId());
realmVideo.setOwnerId(video.getOwnerId());
realmVideo.setPhoto130(video.getPhoto130());
realmVideo.setPhoto320(video.getPhoto320());
realmVideo.setPhoto640(video.getPhoto640());
realmVideo.setPlatform(video.getPlatform());
realmVideo.setTitle(video.getTitle());
realmVideo.setViews(video.getViews());
realmAttachment.setType(type);
realmAttachment.setVideo(realmVideo);
attachmentList.add(realmAttachment);
}
realmMessage.setAttachments(attachmentList);
}
realmResponse.getItems().add(item);
}
}
realmResponse.setCount(itemCount);
realmChats.setResponse(realmResponse);
}
});
}
Works correctly!
Just read in the official documentation about the method insert(), also for storage in the database. I rewrote the setChatsList() method thus:
public void setChatsList(final ChatsModel chatsModel) {
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
realm.insert(chatsModel);
}
});
}
And to my surprise, it worked fine too and the code is less!
But I'm sure that not everything is so smooth, I think that somewhere there is a catch.
Question: What is the difference between insert() and createObject()?
insert()
saves an unmanaged object into the Realm (managed object is no-op), without creating a managed proxy object as return value.
createObject()
creates a managed object in Realm, and returns a proxy to this managed object.
copyToRealm()
saves an unmanaged object into the Realm, with returning a proxy to the created managed object.
The key difference between insert() and copyToRealm() is whether a proxy is returned or not; which means that inserting many items is much more efficient by re-using a single unmanaged object and calling insert() on it with the right parameters.
However, you generally need createObject() if you want to set up relations between objects.
P.S. insert/copyToRealm/createObject(clazz) have a counterpart insertOrUpdate, copyToRealmOrUpdate and createObject(clazz, primaryKeyValue) for objects with primary keys.
Assuming you have primary key as integer
0 is the default value for int fields, so if you have a RealmObject with 0 as the value then, it means that realm.createObject(YourRealmClass.class) will fail with the error mentioned below.
RealmPrimaryKeyConstraintException: Value already exists:
as it will create an object with the default value.
What is the better way to create RealmObjects?
copyToRealmOrUpdate() or insert().
I will recommend you that use copyToRealmOrUpdate() because. it is better to use as it first checks if record exists or not . If record exists then it will update if record does not exits it will insert new record .

Categories