Best practice to store object with generic field into a database - android

I'm implementing an application for private use in Android which request some data from server in JSON format, deselialize it to POJO and need to store objects in a local database. The problem is that Child class contains a generic value, also the value may be primitive type (integer, string, ...) or some custom objects. I tried Realm, but i got NPE. I have read about EAV-Pattern, but I'm not really satisfied with it.
What is the best way to store object containing generic values in a database? I'm not depend on a specific database, but would prefer SQLite.
Simple POJOs of data structure without extends RealmObject:
public class Parent {
public int i;
public Child mChild;
}
A class which contains generic field:
public class Child<T> {
public int i;
public T mValue;
}
A custom object:
public class Value {
public int i;
public String s;
}
An example trying Realm:
Parent p = new Parent();
p.i = 10;
// Child<String> c = new Child<>();
// c.mValue = "I'm a string!";
Child<Value> c = new Child<>();
Value v = new Value();
v.s = "I'm a custom object!";
v.i = 42;
p.mChild = c;
Realm.init(getApplicationContext());
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Parent parent = realm.copyToRealm(p);
}
});

Related

RealmList with Android

Items upon inserting
Items upon retrieving
So here is the thing:
I have an Object Product which extends RealmObject.
Inside the Product object, there is an attribute: RealmList. Bundle also extends RealmObject.
Inside the Bundle Object, there is an attribute: RealmList. ProductBundle extends RealmObject.
Now all those lists are not empty nor null by the time they should be inserted.
After insertion, if I query Realm for a list of Products, I will receive a list that has a list of RealmList. However, inside each of the Bundle items, the RealmList< ProductBundle> productBundles is always empty.
class Product extends RealmObject{
RealmList<Bundle> bundles= new RealmList<Bundle>();
boolean isLocallyAddedToCart;
}
class Bundle extends RealmObject{
RealmList<ProductBundle> productsBundles = new RealmList<ProductBundle>();
}
class ProductBundle extends RealmObject{
String title;
}
Any thoughts on that??
Query:
RealmResults<Product> cartItem = realm.where(Product.class).equalTo("isLocallyAddedToCart", true).findAll();
Inserting:
public void insertAddToCart(#NonNull ArrayList<Product> items) {
try {
Realm realm = getRealmInstance();
realm.beginTransaction();
realm.insertOrUpdate(items);
realm.commitTransaction();
realm.close();
} catch (Exception ex) {
ex.getStackTrace();
}
}
Handling the objects:
RealmList<BundleProductOption> bundleProductOptions = new RealmList<>();
private SparseArray<List<ProductBundle>> optionsFinalSelection = new SparseArray<>();
BundleProductOption bundleProduct = new BundleProductOption();
bundleProduct.setDefaultTitle(bundleProductOption.getDefaultTitle());
bundleProduct.setOptionId(bundleProductOption.getOptionId());
bundleProduct.setParentId(bundleProductOption.getParentId());
bundleProduct.setRequired(bundleProductOption.getRequired());
bundleProduct.setType(bundleProductOption.getType());
RealmList<ProductBundle> productBundles = new RealmList<>();
productBundles.addAll(optionsFinalSelection.get(i));
bundleProduct.setProductsBundle(productBundles);
product.setSelectedOptions(bundleProductOptions);
you must manage your insertion with ID as a primary key in each model.
when u insert or update u must use a primary key for saving all states of your data, and then u can do not use them, but realm need them for arrange insert or update method.
keep me updated with your issue .

Make first letter of child name capital in Firebase

I push data to Firebase using Order object, the question is I want the first letter of every child name capital. I defined the property like "Complain" but in Firebase it still shows as "complain", I dont know how to make it.
The current structure of the Firebase:
The structure I want:
I defined the property like this:
#Data
public class Order implements Serializable {
#SerializedName("Complain")
private String Complain;
public Order() {
Complain = "";
}
public String getComplain() {
return Complain;
}
public void setComplain(String complain) {
Complain = complain;
}
}
I push data to Firebase like this:
Map<String, Object> map = new HashMap<>();
map.put(orderSavePath, order);
reference.updateChildren(map).addOnCompleteListener(listener);
The Firebase JSON serialization name is controlled by the annotation PropertyName.
public class Order implements Serializable {
private String Complain;
public Order() {
Complain = "";
}
#PropertyName("Complain")
public String getComplain() {
return Complain;
}
#PropertyName("Complain")
public void setComplain(String complain) {
Complain = complain;
}
}
The annotation needs to be on both the getter and the setter. Alternatively you can just use public fields and reduce the class to:
public class Order {
#PropertyName("Complain")
public String Complain;
}

Handling ForeignCollection when manually initializing parent entity

I designed a model with two entities : ParentEntity and ChildEntity. I use OrmLite to store them in a database.
I actually get my data from a remote webservice. This is the process I have :
Request the webservice (using retrofit)
Get JSON string in response
JSON string is parsed to JSON model by Gson (thank you again retrofit :))
I convert JSON model to an OrmLite model (no library, I make it myself)
The OrmLite model is given back to the callback waiting for the response of the request
The callback is in charge of calling the DAO to actually store the data
This process works perfectly for simple entities. But a problem appears when I try to manage more complex entities, with a ForeignCollection for example. Actually, I can't achieve the step 4 because I can't create a new ForeignCollection to put my child entities inside it.
I found some answers saying that I should store every child myself, but it will break the step 6 of my workflow.
So the question is :
How can I initialize the ForeignCollection before getting the object from the database ?
I could find a way to change the workflow. But it's only a kind of "work-around" and will create container objects just to achieve this...
The OrmLite schema (simplified)
(The class Entity just have a property id and its getter/setter.)
ParentEntity
#DatabaseTable(daoClass = ParentEntityDao.class)
public class ParentEntity extends Entity
{
#ForeignCollectionField(eager = true)
private ForeignCollection<TimesheetEntry> entries;
public Collection<TimesheetEntry> getEntries()
{
if(entries != null)
{
return Collections.unmodifiableCollection(entries);
}
else
{
return Collections.unmodifiableCollection(new ArrayList<TimesheetEntry>());
}
}
public void addEntry(ChildEntry childEntry)
{
childEntry.setParent(this);
entries.add(childEntry);
}
public void removeEntry(ChildEntry childEntry)
{
entries.remove(childEntry);
}
}
ChildEntity
#DatabaseTable(daoClass = ChildEntityDao.class)
public class ChildEntry extends Entity
{
#DatabaseField(foreign = true, canBeNull = false)
private ParentEntity parentEntity;
public void setParentEntity(ParentEntity parentEntity)
{
this.parentEntity = parentEntity;
}
public ParentEntity getParentEntity()
{
return parentEntity;
}
}
JSON schema (simplified)
Parent entity
public class JsonParentEntity
{
private List<JsonChildEntity> entries;
// Getter/setter
}
Child entity
public class JsonChildEntity
{
private String name;
// Getter/setter
}

Data change in realm android

In my android app, I persist a workout object to realm. In one of my activities, I create an object with this code:
realm.beginTransaction();
Workout w = realm.createObject(Workout.class);
w.setmWorkoutId(UUID.randomUUID().toString());
realm.commitTransaction();
Here is my workout class:
public class Workout extends RealmObject{
private String mWorkoutId;
private int restSecsLeft;
private boolean prevSetOver = true;
private boolean workoutOver = false;
public Workout(){}
public String getmWorkoutId() {
return mWorkoutId;
}
public void setmWorkoutId(String mWorkoutId) {
this.mWorkoutId = mWorkoutId;
}
public int getRestSecsLeft() {
return restSecsLeft;
}
public void setRestSecsLeft(int restSecsLeft) {
this.restSecsLeft = restSecsLeft;
}
public boolean getPrevSetOver() {
return prevSetOver;
}
public void setPrevSetOver(boolean prevSetOver) {
this.prevSetOver = prevSetOver;
}
public boolean getWorkoutOver() {
return workoutOver;
}
public void setWorkoutOver(boolean workoutOver) {
this.workoutOver = workoutOver;
}
}
I have a service that runs after a workout is created, and after debugging odd behavior, have found an instance where the value of prevSetOver that is saved in a workout RealmObject is different than the value returned from w.getPrevSetOver(). I am not sure how this is happening--I do not change the value of the variable prevSetOver after an object is instantiated. I am a new realm user and do not understand how this is happening. The picture I have attatched is a screenshot of the w.prevSetOver() method and the RealmObject having different values.
There are more variables in the debugger in this screen, I left most of them out in my post for simplicity's sake.
It is the right behaviour of Realm.
Realm generates Proxy object which inherit from your Workout when compiling. And read/write data from/to Realm is actually implemented by the Proxy Object through overriding getters/setters. The original Object's member field won't be changed by Realm.
When Realm.createObject() get called, it does return a Proxy object, whose member fields are not what you expected.
You still can create a instance of the original model object which we call it standalone object (means it is not managed by Realm) by calling Workout w = new Workout(). This would act just like normal Java object. And you still can copy it to Realm by calling w = realm.copyToRealmOrUpdate(w). Notice we changed the w's value to the return value. The function will return a Proxy object which is managed by Realm now.
realm.beginTransaction();
Workout w = realm.createObject(Workout.class);
w.setmWorkoutId(UUID.randomUUID().toString());
realm.copyToRealm(w); //<-- u need
realm.commitTransaction();
is better add Primarykey, changed u String variable to long
#PrimaryKey
private long mWorkoutId;

In Java, How do I cast a Realm Object into the class I wish to write to the Database?

I am currently working on a project where my Database has a very large number of tables (Approx 60 total). I am working to create the Database Helper class that will function as the writer/ reader to/ from the database. And example of my write method would be this:
public static void executeInsertInto(final Context context, final UserData passedObject){
//Generate a realm object from the context
Realm realm = Realm.getInstance(context);
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
//Begin a transaction, create a new item from the passed object, and commit.
realm.beginTransaction();
UserData itemToWrite = realm.copyToRealm(passedObject);
realm.commitTransaction();
}
});
//Close the realm object to prevent leaks
if (realm != null) {
realm.close();
}
}
As you can see from the code, I manually created an object of type UserData; The problem is, I either have to repeat this a total of 60 times (for the many types of objects / tables) or come up with a better way. This leads me to my question, is there a way to create an object using the type of passed object to determine the class? Something like the code below did not want to work as I am doing something fundamentally wrong with my Java code:
public static void executeInsertInto(final Context context, final RealmObject passedObject){
//Generate a realm object from the context
Realm realm = Realm.getInstance(context);
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
//Begin a transaction, create a new item from the passed object, and commit.
realm.beginTransaction();
//The below line is flawed in that you cannot create an object that way,
//But hopefully it illustrates what I am trying to accomplish
passedObject.getClass() itemToWrite = (passedObject.getClass()) realm.copyToRealm(passedObject);
realm.commitTransaction();
}
});
//Close the realm object to prevent leaks
if (realm != null) {
realm.close();
}
}
Anyone have any Ideas? I would greatly appreciate it!
EDIT:
Just to clarify further, Take Realm out of the picture for one second, assuming I have two classes (Class Car and Class Bike), both of which extend to Class Transportation, if I am passed class transportation as an argument, how can I determine which type is being passed and then once I do, how do I create a new object with that information?
-Sil
All Realm classes extends RealmObject so you can use generics to accomplish it:
public static <T extends RealmObject> void executeInsertInto(final Context context, final T passedObject){
Realm realm = Realm.getInstance(context);
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
T itemToWrite = realm.copyToRealm(passedObject);
}
});
realm.close();
}

Categories

Resources