Polymorphism in Firebase - java

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.

Related

Unable to create records using custom generator strategy for getter names

I'm on jOOQ 3.13.1, dropwizard 2.0.7. To make jOOQ and dropwizard together, I am using (https://droptools.bendb.com/jooq/). I am using custom generation strategy to maintain camel case for my setters and getters. The names are coming in as expected.
The record objects have data for their respective columns. However, I keep on getting errors from my database that I am trying to set "null" on a non-null column.
I see this issue only when I am trying to create a new record. Updating records work just fine.
ERROR [2021-03-18 14:58:05,363] com.bendb.dropwizard.jooq.jersey.LoggingDataAccessExceptionMapper: Error handling a request: 9f65c0316d996ebb
! org.postgresql.util.PSQLException: ERROR: null value in column "createdAt" of relation "failures" violates not-null constraint
! Detail: Failing row contains (265, null, Some callback, User account not found, null, null, null).
If I print the record, it looks like this:
+------+------+--------------+--------------------------------------------------+------------------------------+------------------------------+------+
| id|userId|action |error |createdAt |updatedAt |status|
+------+------+--------------+--------------------------------------------------+------------------------------+------------------------------+------+
|{null}|{null}|*Some callback|*User account not found|*2021-03-18 14:58:05,363|*2021-03-18 14:58:05,363|{null}|
+------+------+--------------+--------------------------------------------------+------------------------------+------------------------------+------+
My getter names are:
"getId", "getUserId", "getAction", "getError", "getCreatedAt", "getUpdatedAt", "getStatus".
For columns that are in lowercase, I see no issues. The issue if for places where the column names are in CamelCase.
The class looks something like:
public class FailureDao {
private final DSLContext database;
public FailureDao(DSLContext database) {
this.database = database;
}
public void storeFailure(FailureRecord failure) {
database.newRecord(FAILURES, failure).store();
}
}
For code generation, I am following the documentation here https://www.jooq.org/doc/3.13/manual/code-generation/codegen-generatorstrategy/
My generator class looks something like:
public class AsInDatabaseStrategy extends DefaultGeneratorStrategy {
#Override
public String getJavaIdentifier(Definition definition) {
return definition.getOutputName().toUpperCase();
}
#Override
public String getJavaSetterName(Definition definition, Mode mode) {
return "set" + StringUtils.toUC(definition.getOutputName());
}
#Override
public String getJavaGetterName(Definition definition, Mode mode) {
return "get" + StringUtils.toUC(definition.getOutputName());
}
}
I found the issue. Turns out, it was explained on https://groups.google.com/g/jooq-user/c/1iy0EdWe_T8/m/YN9PEsIF4lcJ. My workaround was to use a jOOQ generated POJO. To create a new record, instead of passing an object of Record class, I am now passing an object of the POJO class.

Getter Pattern Within Class?

I have a field in a class that should only be accessed directly from a getter. As an example...
public class CustomerHelper {
private final Integer customerId;
private String customerName_ = null;
public CustomerHelper(Integer customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
if(customerName_ == null){
// Get data from database.
customerName_ = customerDatabase.readCustomerNameFromId(customerId);
// Maybe do some additional post-processing, like casting to all uppercase.
customerName_ = customerName_.toUpperCase();
}
return customerName_;
}
public String getFormattedCustomerInfo() {
return String.format("%s: %s", customerId, getCustomerName());
}
}
So even within the class itself a function like getFormattedCustomerInfo should not be able to access it via customerName_. Is there a way to enforce a class not access a field directly aside from the provided getter function?
There is no such mechanism in Java (or at least I think there should not be). If you are sure that getFormattedCustomerInfo should be prohibited from direct access to customerName_, create another class and compose them.
I would recommend CustomerInfoFormatter.
Also, I would change customerName_ to customerName as the language supports privacy by explicit declaration and it is not needed to add more indicators.
It looks like you are trying to cache the database value, and want to protect against accessing a value which has yet to be cached.
If this is true, then the variable customerName_ should not exist in the CustomerHelper class; the cached value should exist closer to the database.
The method customerDatabase.readCustomerNameFromId(customerId) should first look at a cache, and if the cache is empty, call the database and cache the result.
Effectively, customerName_ becomes a value in the cache: Map<Integer, String> cache where the key is customerId.

Pattern for persisting data in Realm?

My issue is how to organize the code. Let say I have a User class
public class User extends RealmObject {
#PrimaryKey
private String id;
#Required
private String name;
public User() { // per requirement of no args constructor
id = UUID.randomUUID().toString();
}
// Assume getter & setter below...
}
and a Util class is needed to handles the save in an asynchronous manner since RealmObjects cannot have methods other than getter/setter.
public class Util {
public static void save(User user, Realm realm) {
RealmAsyncTask transaction = realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealm(user); // <====== Argument needs to be declared final in parent method's argument!
}
}, null);
}
}
The intention is to put save() in a Util class to prevent spreading similar save code all over the code-base so that every time I wanted to save I would just call it as such:
User u = new User();
u.setName("Uncle Sam");
Util.save(u, Realm.getDefaultInstance());
Not sure if this affects performance at all, but I was just going to save all fields overwriting what was there except for the unique id field every single time.
The problem is that I now need to set the "user" argument as final in the Util.save() method, which means I cannot pass in the object I need to save other than once.
Is there a different way of handling this? Maybe a different pattern? Or am I looking at this all wrong and should go back to SQLite?
Why is it a problem to set public static void save(final User user, Realm realm) ? It just means you cannot reassign the user variable to something else.
That said, the existence of a save() method can be a potential code smell as you then spread the update behaviour across the code base. I would suggest looking into something like the Repository pattern (http://martinfowler.com/eaaCatalog/repository.html) instead.
Realm is actually working on an example showing how you can combine the Model-View-Presenter architecture with a Repository to encapsulate updates which is a good pattern for what you are trying to do here. You can see the code for it here: https://github.com/realm/realm-java/pull/1960

Getting hibernate persistent object at the time of pre update event

I am implementing pre update event listener in java hibernate 4.3.
I need to get old persistent object value before update occures.
I have tried using event.getOldState() in PreUpdateEventListener. But it gives Object[] as return type. I want the persistent object as return value.
How to get complete persistent object in preUpdateEvent?
The preUpdateEventListener is implemented correctly.
Just need to get Complete persisted object instead i get Object[].
Also tried event.getSession().get(id,persisted.class); //this gives new object as session has set new object to update
Below is code that gives Object[]
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.event.spi.PreUpdateEvent;
public class MyEventListener implements PreUpdateEventListener {
public void onPreUpdate(PreUpdateEvent event) {
Object newEntity=event.getEntity(); //Gives new Object which will be updated.
Object[] oldEntity=evetn.getOldState(); //gives old Object[] which can't be converted to persisted Object
//Code here which will give me old persisted objects, hibernate fetches object in array format.
}
}
If i remember well the object array contains all attribute values of given entity :
the index of the associated property can be resolved using the property name array
String[] propertyNames = event.getPersister().getEntityMetamodel.getPropertyNames();
this link may be usefull
I am not sure how listeners work with pure Hibernate, but if you use JPA event listeners, the entity is passed as a parameter to the listener method:
public class MyUpdateListener {
#PreUpdate
public void onPreUpdate(MyEntiy e) {
e.getAttribute();
// do something
}
...
If you define a listener method inside the entity, you can simply access the state of this
#PreUpdate
public void onPreUpdate() {
getAttribute();
// do something
}

Inherited class doesn't get stored in Windows Azure Mobile Service

I am in a process of creating a library for Windows Azure. So, here is a simple generic method to insert a new record:
public <TEntity extends SyncableBase> void addRemoteItem(TEntity itemToAdd) {
MobileServiceTable<TEntity> remoteTable = (MobileServiceTable<TEntity>)mobileServiceClient.getTable(itemToAdd.getClass());
Gson gson = new Gson();
String json = gson.toJson(itemToAdd);
remoteTable.insert(itemToAdd, new TableOperationCallback<TEntity>() {
public void onCompleted(TEntity entity, Exception exception, ServiceFilterResponse response) {
if (exception == null) {
Log.e("SuccessMe", "Success");
// Insert succeeded
}
else {
Log.e("SuccessMe", "Nah "+ exception.getMessage());
// Insert failed
}
}
});
}
Now, here is my SyncableBase class:
public class SyncableBase {
#SerializedName("Bingo")
private int localId;
//#SerializedName("id")
private String remoteId;
private boolean isDeleted;
}
And my ToDoItem class:
public class ToDoItem extends SyncableBase {
private String name;
}
Now, the problem is: This fails with Error processing request. But if I don't extend ToDoItem from SyncableBase and move all those members directly to the former, everything works just fine. As can be seen, I tried serializing my Java object just before calling inset. The serialized json is exactly the same in both the cases. What am I doing wrong?
After days of debugging, I have come up with a potential problem and it's definite solution. This holds valid for the Android Azure SDK valid at the time of writing this. A couple of notes:
For seamless transactions, the id member must be present in the inherited class and not the super class. While validating the object, Azure SDK uses reflection and tries to find a filed with name (or serialized name) equal to id or Id. Somehow, the member isn't found if it is present in super class and we get error.
GSON (the thing which serializes Java object to JSON) is configured inside SDK so that it serializes even the null members. So, when there are no columns in WAMS table (fresh table) and try to insert an item with null fields, the error is thrown. The filed must hold a value so that the type of corresponding column to be generated can be determined. A new field with null value will give you an error.
Here's an example of an item being put in a fresh table.
{
"id": "Awesome unique id",
"name": Beautiful Wallpaper",
"description": null
}
Here, the WAMS would know that it has to generate a column called description; however, it wouldn't know the type of this column. Hence, first object must always have non-null values.
In my particular case, both the problems are there. Taking care of these things solved them.

Categories