Given the following POJO example which stores local fields applicable only to the app running right here, nobody else whom also use the Firebase data:
#IgnoreExtraProperties
public class DispatchModel {
private String isotimestamp;
private String date_time;
private String event_uuid;
//...
private String locallyCreatedValue;
//...constructor, getters, setters
}
Given my Firebase data has the first three fields stored in it, and the locallyCreatedValue is added to my POJO during app runtime, is there a way to automatically fetch all the locally added content from a POJO instance and apply to the new instance when an update from onChildChanged event happens?
As it is right now, I'll have to manually obtain all the local field values and set them on the new instance:
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
DispatchModel newModel = dataSnapshot.getValue(DispatchModel.class);
// get list index of expiring instance
// get instance of old item at list index
// index = ...
// old = ...
// repeat this for every local item :-/
newModel.setLocallyCreatedValue(old.getLocallyCreatedValue);
dispatchList.set(index, newModel);
}
I plan on having quite a few local fields, is this my only option? Are there any functions Firebase offers that makes their automatic object instantiation more friendly to my extras? I'm not keen on creating distinct POJOs to track the Firebase POJOs in parallel. That lends to data errors from decoupled data updates and careful schedules for execution.
If all of your locally created values are expressed as standard getters and setters in your POJO, you are either going to have to copy them manually, or write some fairly intense Java reflection code to inspect the class, somehow figure out which properties are local (annotation? inclusion/exclusion from a known list?) and should be copied over, then actually do that work. You will not be able to get this "for free" using some utility (unless there happens to be some third party library that has solved this specific problem, and I doubt it).
Consider instead maybe storing your locally created values as a Map, then simply copying that map wholesale between objects. But then you have unknown types for the values if they're all different.
Or rewrite your code in JavaScript, which has easy property enumeration. :-)
Related
I am kind of a beginner in Java. I have this college project where we are asked to build a train booking system (desktop). As part of the app, the admin can add and edit new routes. I want to store those instances of different routes somewhere, but how? I want to be able to add as many as I want, but lists and arrays require for a size to be determined. How can I store indefinite instances of an object efficiently? This is the data I want to store for each instance:
int routeId;
String deptPoint;
String destPoint;
String transpMode;
int vehicleId;
Note: we must use Java data types, no DBs allowed.
Some help would be appreciated! Thanks :)
but lists and arrays require for a size to be determined.
Incorrect. Arrays have a set size, but not lists. A List implementation (if mutable) supports automatic dynamic resizing, up to a limit of two billion items or running out of memory.
Define your class. Here we use the records feature in Java
16+ for brevity. But if you need mutable objects, declare a conventional class instead.
record Route( int routeId, String deptPoint, String destPoint, String transpMode, int vehicleId ) {}
Declare a list to hold objects of that class.
List< Route > routes = new ArrayList<> () ;
Instantiate Route objects, and collect.
routes.add( new Route( … ) ) ;
In fact, Java lists are not requiring predetermined size, they will change as you add elements and remove them, so they're perfectly fine to store Java objects. Thing you didn't mention is do you need to persist it or not, if you don't need to then you can just do that. If you need to, you'll need database or store it to the file on your machine.
You could build an object that have this fields and put it in a List:
public class Route {
int routeId;
String deptPoint;
String destPoint;
String transpMode;
int vehicleId;
}
As deHaar suggested, one option would be to store your values in a text file with JSON format. You could use gson to convert to/from JSON really easy. You then only have to implement the mechanism to store this JSON in a local file following this example.
We are making a Firestore query by using collection name, so I thought to store collection name in a static class, or in the same class something like:
private static final String collUsers = "users";
and then use something like db.collections(collUsers).documents()
So is it safe to store collection names in fields?
Is it safe to store the collection name in the class of Firestore Project class file?
Sure, it is. The collection names are always Strings. You can use them directly in your reference:
db.collections("users").documents()
Or you can save that name in a variable. However, if you do that, that variable becomes a constant, as the name of the collection cannot be changed. So most probably the variable name might look like this:
public static final String USERS_COLLECTION = "users";
So the problem is not that you keep these names in your project file, the problem is how you read, write, and delete the data that exists there. In that case, you should always secure that data using Firestore Security Rules.
Yes, it's safe to keep your query for firestore open, until you have firestore rules in place which guard your data against any misuse and edits.
If you want to know more about securing your data: Read this
I am migrating my app to use Firebase Firestore, and one of my models is very complex (contains lists of other custom objects). Looking at the documentation, on how to commit a model object as a document, it looks like you simply create your model object with a public constructor, and getters and setters.
For example from the add data guide:
public class City {
private String name;
private String state;
private String country;
private boolean capital;
private long population;
private List<String> regions;
public City() {}
public City(String name, String state, String country, boolean capital, long population, List<String> regions) {
// getters/setters
}
Firestore automatically translates this to and from and document without any additional steps. You pass an instance to a DocumentReference.set(city) call, and retrieve it from a call to DocumentSnapshot.toObject(City.class)
How exactly does it serialize this to a document? Through reflection? It doesn't discuss any limitations. Basically, I'm left wondering if this will work on more complex models, and how complex. Will it work for a class with an ArrayList of custom objects?
Firestore automatically translates this to and from and document without any additional steps. How exactly does it serialize this to a document? Through reflection?
You're guessing right, through reflection. As also #Doug Stevenson mentioned in his comment, that's very common for systems as Firebase, to convert JSON data to POJO (Plain Old Java Object). Please also note that the setters are not required. If there is no setter for a JSON property, the Firebase client will set the value directly onto the field. A constructor-with-arguments is also not required. While both are idiomatic, there are good cases to have classes without them. Please also take a look at some informations regarding the existens fo the no-argument constructor.
It doesn't discuss any limitations.
Yes it does. The official documentation explains that the documents have limits. So there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:
Maximum size for a document: 1 MiB (1,048,576 bytes)
As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text, you can store pretty much but as your array getts bigger (with custom objects), be careful about this limitation.
Please also note, that if you are storing large amount of data in arrays and those arrays should be updated by lots of users, there is another limitation that you need to take care of. So you are limited to 1 write per second on every document. So if you have a situation in which a lot of users al all trying to write/update data to the same documents all at once, you might start to see some of this writes to fail. So, be careful about this limitation too.
Will it work for a class with an ArrayList of custom objects?
It will work with any types of classes as long as are supported data type objects.
Basically, I'm left wondering if this will work on more complex models, and how complex.
It will work with any king of complex model as long as you are using the correct data types for your objects and your documents are within that 1 MIB limitation.
I doesn't understand how to use repositoryItem in ATG. How do I need construct customized logic on it.
Do I need to create usual JavaBean over repositoryItem or I need to use it as is?
I will try to explain:
Logic on repositoryItem:
RepositoryItem store = getRepository().getItem(..);
String address = store.getPropertyValue(..);
Logic on JavaBean:
class StoreBean {
String address;
StoreBean(RepositoryItem store) {
address = store.getPropertyValue(..);
}
}
Then I can use StoreBean how I want, to get it fields(lazy load for them, for example).
What will be best practices in ATG?
It is a matter of preference.
What you do not get with RepositoryItem objects is strong type checking. You must either make assumptions about the type of RepositoryItem you are working with or you have to do manual checks in your code (see example below). Additionally, since the RepositoryItem properties are stored as a metadata, you have to know 1) the actual names of the properties from the XML repository descriptor and 2) you need to know the types, which requires type casting (Example: String firstName = (String) item.getProperty("firstName");) Here is an example of a validation to ensure the RepositoryItem object is of type "sku":
RepositoryItemDescriptor skuItemDescriptor = getCatalogTools().getCatalog().getItemDescriptor(getCatalogTools().getBaseSKUItemType());
if (!RepositoryUtils.isTypeOfItemDesc(itemDescriptor, skuItemDescriptor)) {
throw new IllegalArgumentException("RepositoryItem must be of type " + getCatalogTools().getBaseSKUItemType());
}
If you take the approach of not using "JavaBeans", then you are increasing the risk of having runtime errors in your application. My suggestion is that you have a healthy balance between using RepistoryItem objects and wrapper objects. For critical items you plan to use in a large amount of your code base, I suggest using a wrapper object.
I suggest that if you create wrapper objects, that for consistency, you follow the same design pattern that Oracle Commerce uses. For example, the "order" item is wrapped by OrderImpl and implements the ChangedProperties interface.
public class OrderImpl
extends CommerceIdentifierImpl
implements Order, ChangedProperties
http://docs.oracle.com/cd/E52191_03/Platform.11-1/apidoc/atg/commerce/order/OrderImpl.html
ATG out of box repository implementations do not use JavaBeans for the most part. One big disadvantage of using JavaBeans and lazy loading them into memory will be to lose many repository caching features and will increase your memory footprint. For instance you will not be able to monitor your cache statistic or invalidate cache periodically. You will also have overheads of instantiations when you have huge repotiroyitem result set from a query.
Instead you can also use DynamicBean which lets you refer to repository properties similar to java beans for instance Profile.city.
If you only want to wrap them so that developers don't accidentally parse them incorrectly, you can write a util class per repository for various types of ready write operations and centralize your type safety.
Although appengine already is schema-less, there still need to define the entities that needed to be stored into the Datastore through the Datanucleus persistence layer. So I am thinking of a way to get around this; by having a layer that will store Key-value at runtime, instead of compile-time Entities.
The way this is done with Redis is by creating a key like this:
private static final String USER_ID_FORMAT = "user:id:%s";
private static final String USER_NAME_FORMAT = "user:name:%s";
From the docs Redis types are: String, Linked-list, Set, Sorted set. I am not sure if there's more.
As for the GAE datastore is concerned a String "Key" and a "Value" have to be the entity that will be stored.
Like:
public class KeyValue {
private String key;
private Value value; // value can be a String, Linked-list, Set or Sorted set etc.
// Code omitted
}
The justification of this scheme is rooted to the Restful access to the datastore (that is provided by Datanucleus-api-rest)
Using this rest api, to persist a object or entity:
POST http://datanucleus.appspot.com/dn/guestbook.Greeting
{"author":null,
"class":"guestbook.Greeting",
"content":"test insert",
"date":1239213923232}
The problem with this approach is that in order to persist a Entity the actual class needs to be defined at compile time; unlike with the idea of having a key-value store mechanism we can simplify the method call:
POST http://datanucleus.appspot.com/dn/org.myframework.KeyValue
{ "class":"org.myframework.KeyValue"
"key":"user:id:johnsmith;followers",
"value":"the_list",
}
Passing a single string as "value" is fairly easy, I can use JSON array for list, set or sorted list. The real question would be how to actually persist different types of data passed into the interface. Should there be multiple KeyValue entities each representing the basic types it support: KeyValueString? KeyValueList? etc.
Looks like you're using a JSON based REST API, so why not just store Value as a JSON string?
You do not need to use the Datanucleus layer, or any of the other fine ORM layers (like Twig or Objectify). Those are optional, and are all based on the low-level API. If I interpret what you are saying properly, perhaps it already has the functionality that you want. See: https://developers.google.com/appengine/docs/java/datastore/entities
Datanucleus is a specific framework that runs on top of GAE. You can however access the database at a lower, less structured, more key/value-like level - the low-level API. That's the lowest level you can access directly.
BTW, the low-level-"GAE datastore" internally runs on 6 global Google Megastore tables, which in turn are hosted on the Google Big Table database system.
Saving JSON as a String works fine. But you will need ways to retrieve your objects other than by ID. That is, you need a way to index your data to support any kind of useful query on it.