I'm creating an app in Android Studio, which connects to a Cloud Firestore database. In the database I have the following structure:
Myclass
- name = "test"
- subclass
- 0 = "String 1"
- 1 = "String 2"
The class itself is declared like this (irrelevant bits removed):
public class Myclass {
private String name;
private String[] subclass;
// CONSTRUCTOR
public Chart() {}
//GETTERS
public String getName() { return this.name; }
// SETTERS
public void setSubclass(String[] thisSubclass) { this.subclass = thisSubclass; }
}
In the activity, the Myclass object is set up like this (again, irrelevant bits removed):
public class MyclassActivity {
DocumentReference docRef;
Myclass myItem;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up database connection, read in itemId etc...
// ...omitted for clarity...
docRef = databaseRef.collection("myclass").document(itemId);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
myItem = document.toObject(Myclass.class);
}
}
}
}
This reads in the Myclass object, with the name set correctly, but the subclass object doesn't get set up - it's still null.
In the debug console there's the following message:
No setter/field for subclass found on class path.to.app.Myclass
The 'setSubclass' function is greyed out, as if it's never used. I'm sure the problem is something obvious, but I can't see it.
Your problem right now its that your class name must be the same than the constructor. Also you need to add a getter to your subclass parameter.
public class Chart {
private String name;
private String[] subclass;
public Chart() {
//Default empty constructor, required for Firebase.
}
public Chart(String name, String[] subclass) {
this.name = name;
this.subclass = subclass;
}
public String getName() {
return this.name;
}
public String[] getSubclass() {
return subclass;
}
}
In other hand, you don't need to add the setters. They are not required. Firebase will set the value into the field. But you should add them if you're going to interact with the class from outside.
There will be some cases where you want to have different names on your parameters, maybe because you want to follow a camelCase nomenclature or something. If that's the case you can use the annotation #PropertyName to provide a different name in your database and keep your model as you want. For example:
public class Chart {
#PropertyName("name")
private String mName;
#PropertyName("subclass")
private String[] mSubclass;
public Chart() {
}
#PropertyName("name")
public String getmName() {
return mName;
}
#PropertyName("subclass")
public String[] getmSubclass() {
return mSubclass;
}
}
You have two errors in your model class. First one would be the name of the constructor which is different than the name of the class and should be the same. And the second, for the subclass field you have only defined the setter but without a getter.
Your Myclass class should look like this:
public class MyClass {
private String name;
private String[] subclass;
public MyClass() {}
public MyClass(String name, String[] subclass) {
this.name = name;
this.subclass = subclass;
}
public String getName() { return name; }
public String[] getSubclass() { return subclass; }
}
Setters are not not required. If there is no setter for a JSON property, the Firebase client will set the value directly onto the field, that's why is called idiomatic. If you need them explicitly in your code, just add the following setters to your model class like this:
public void setName(String name) { this.name = name; }
public void setSubclass(String[] subclass) { this.subclass = subclass; }
Regarding the use of arrays in the Cloud Firestore database, please see my answer from this post.
Related
I have two classes where one class inherits the other one as given below:
public class UserData {
protected final String emailAddress;
protected final String name;
public UserData(final String emailAddress, final String name) {
this.emailAddress = emailAddress;
this.name = name;
}
public Optional<String> getEmailAddress() {
return Optional.ofNullable(this.emailAddress);
}
public Optional<String> getName() {
return Optional.ofNullable(this.name);
}
}
public class EmployeeData extends UserData {
protected final String designation;
public EmployeeData(
final String emailAddress,
final String name,
final String designation
) {
super(emailAddress, name);
this.designation = designation;
}
public Optional<String> getDesignation() {
return Optional.ofNullable(this.designation);
}
}
I need to create method in another class that can return either one of these objects and have all getters accessible. I already tried making the return type UserData for both kinds of objects (example given below) but that way, I cannot access the getDesignation getter for EmployeeData. Is there a better way inheritance can be setup to avoid this problem where I cannot access child-specific properties?
public UserData getData() {
if (...some condition) {
return new EmployeeData("address#provider.com", "myName", "Dev")
}
else {
return new UserData("address#provider.com", "myName");
}
}
I did look into these stackoverflow questions but couldn't quite figure it out for my use case
C# how to make a function that can return either child or parent class
What's the equivalent of C# IEnumerable in Java? The covariant-capable one, not the Iterable
Because the object we are returning is of type UserData, we will be unable to call methods that are added within the child class, EmployeeData. You could create the getDesignation() method inside the UserData class and have it return an empty optional object.
public Optional<String> getDesignation() {
return Optional.empty();
}
In this case, you can now override the method within the EmployeeData class to return designation as an Optional like this,
#Override
public Optional<String> getDesignation() {
return Optional.ofNullable(this.designation);
}
Now you will have access to the getDestination() method from returned object of getData(), but you will have to be careful and understand that if the returned type is of UserData, then when calling getDesignation() you will be receiving an Optional.empty() object.
I have two java classes as follows
public class A implements Serializable {
private String name;
private List<String> nameList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getNameList() {
return nameList;
}
public void setNameList(List<String> nameList) {
this.nameList = nameList;
}
}
public class B implements Serializable {
private String name;
private List<String> nameList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getNameList() {
return nameList;
}
public void setNameList(List<String> nameList) {
this.nameList = nameList;
}
}
Now I have an object of class A, Aobj, with both the fields initialized. I am using BeanUtils.copyProperties(Bobj, Aobj), but only the name field value is copied into the Bobj however Bobj.nameList is null. Is there a way to deep copy a object so that it copies all the fields including Collections like List, Map etc.
I somewhere heard about Dozer, not sure if that is meant for this purpose.
It is a bit strange that you have 2 different classes that are absolutely identical. But regardless, in order to deep copy one to another just write a 2 static methods in some Util class. One method will take class A and return class B and another will take B and return class A. Do your deep copying by yourself. Also, you can create class C that is the same as your classes A and B and then make your classes A and B just empty classes each extending C. It would give you the same structure, but would make your copying logic easier as you can just work with both A and B as instances of C.
In AppEngine I need to have an entity Diagram that contains an id, title and a variable list of elements of inner class Box, each one with id and description.
Please find below the definition. However, at time of defining the EntityProxy List getter and setter: "The type java.util.List<Box> cannot be used here".
DIAGRAM.java
#Entity
public class Diagram extends DatastoreObject {
public class Box {
private String boxId;
private String description;
public String get_id() {
return boxId;
}
public void set_id(String boxId) {
this.boxId = boxId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Indexed private String diagramId; // Primary key
#Indexed private String title;
#Embedded private List<Box> boxes;
public String get_id() {
return diagramId;
}
public void set_id(String diagramId) {
this.diagramId = diagramId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void setBoxes(List<Box> boxes) {
this.boxes = boxes
}
public List<Box> getBoxes() {
return boxes;
}
}
DIAGRAMPROXY.java
[...]
List<Box> getBoxes();
void setBoxes(List<Box> boxes);
[...]
Your inner class must be static. Nonstatic inner classes have an implicit link to an instance of the outer class, which would be really confusing from the perspective of loading and saving entities to the datastore.
Confusing, you have a Collection<Box> in the Box class? Doesnt sound right.. Anyways the inner Box class must be market static or be moved to a different file. Use the #Embed (version 4.0) annotation on the Box class.
Also, assuming DatastoreObject is the base of all your entities, you can make DatastoreObject as an #Entity and all its sub classes as an #EntitySubClass (index = true). Obviously all sub entities would be be saved under the same 'kind' (DatastoreObject) in the datastore.
Let's assume I have this class :
#EntityListeners({MyListener.class})
class MyClass {
String name;
String surname;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return name;
}
public void setSurname(String name) {
this.name = name;
}
public void save() {
JPA.em().persist(this);
return this;
}
public void update() {
JPA.em().merge(this);
}
public static MyClass findById(Long id) {
return JPA.em().find(MyClass.class, id);
}
}
Now in my MyListener class I'm trying to figure out the previous value MyClass instance versus the new value that is about to get saved(updated) to database. I do this with preupdate metdhod :
#PreUpdate
public void preUpdate(Object entity) {
...some logic here...unable to get the old object value in here
}
So assuming I have a MyClass objects instance with name and surname :
MyClass mycls = new MyClass();
mycls.setName("Bob");
mycls.setSurname("Dylan");
mycls.save();
This wasn't picked up by listener which is ok because I'm listening only to updates.
Now if I were to update this instance like so :
MyClass cls = MyClass.findById(someLongId)
cls.setSurname("Marley");
cls.update();
So this triggers the preUpdate method in mylistener and when I try to :
MyClass.findById(someLongId);
When I debug I already get the newly updated instance but the update hasn't happened yet because when I check in database in column surname it's still Dylan.
How do I get the value from database in my preUpdate method and not the one I just updated?
I think an easy way is to save the previous value in a transient variable that JPA will not persist.
So just introduce a variable previousSurname and save the actual value before you overwrite it in the setter.
If you want to save multiple properties it would be easy if your class MyClass is Serializable.
If so add a post load listener
public class MyClass implements Serializable {
#Transient
private transient MyClass savedState;
#PostLoad
private void saveState(){
this.savedState = SerializationUtils.clone(this); // from apache commons-lang
}
}
But be aware that the savedState is a detached instance.
You can then access the previous state in your EntityListener.
You can also move the PostLoad listener to the EntityListener class. But then you need access to the savedState field. I recommend to make it either package scoped or use a package scoped accessor and put MyClass and MyListener in the same package,
public class MyListener {
#PostLoad
private void saveState(MyClass myClass){
myClass.saveState(SerializationUtils.clone(myClass)); // from apache commons-lang
}
}
public class MyClass implements Serializable {
#Transient
private transient MyClass savedState;
void saveState(MyClass savedState){
this.savedState = savedState;
}
}
I'm trying to figure out how to dynamically call a method. I have a string that describes the method name, but I'm not sure how to do it. I thought this could be done with reflection, but haven't had any success. Example
set.add(vehicleConfiguration.getVehicleYear.getName());
set.add(vehicleConfiguration.getVehicleMake().getName());
set.add(vehicleConfiguration.getVehicleModel().getName());
You'll notice all the method calls are the same with the exception of the getVehicleYear, etc
I have a string that describes the method names, just not sure how to use it.
I got as far as this with reflection, but failed.
set.add(Class.forName("VehicleConfiguration").getMethod("vehicleMake", null).getName());
Thanks in advance.
The class you are looking for is Method. Please read the appropriate javadoc carefully.
You can get a method with, for example
// assumign `getVehicleMake` is the name of the method and it accepts no parameters
Method method = VehicleConfiguration.class.getMethod("getVehicleMake");
// VehicleConfiguration.class can be replaced by
// Class.forName("VehicleConfiguration")
// if VehicleConfiguration is the fully qualified, ie. with packages, name of the class
// other you need Class.forName("com.yourpackage.VehicleConfiguration")
You then need to invoke() this method on an instance of your class.
VehicleConfiguration instance = new VehicleConfiguration();
Object returnObject = method.invoke(instance); // assuming no parameters
To then call getName(), you need to cast the returned object to the type that has the method. Assuming getMake() is a method of the type VehicleMake, call it like this
((VehicleMake)returnObject).getMake();
You have to use actual method name: getVehicleMake, not vehicleMake.
Additionally, if you're using this as anything other than an exercise, don't roll your own. Use Commons BeanUtils or Spring's BeanWrapper.
Expanding on my comment, As all the methods you showed have a getName() method, let's create a simple class which defines this:
class Nameable
{
private String name;
public Nameable(final String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
Now when you create the object for Make, Model and Year, they can all use this class so they can be used interchangeably, and can then be combined into a Car:
class Car
{
public final Nameable make;
public final Nameable model;
public final Nameable year;
public Car(Nameable make, Nameable model, Nameable year)
{
this.make = make;
this.model = model;
this.year = year;
}
public Nameable getInfo(final String info)
{
switch(info)
{
case "make": return this.make;
case "model": return this.model;
case "year": return this.year;
}
return null;
}
}
Then a simple implementation would be:
class PaganiZonda2006 extends Car
{
public PaganiZonda2006()
{
super(new Nameable("Pagani"), new Nameable("Zonda"), new Nameable("2006"));
}
}
And finally, when you want to get the information out, you can read it like so:
public static void main(String[] args)
{
Car car = new PaganiZonda2006();
System.out.println(car.getInfo("make").getName()); //Pagani
System.out.println(car.getInfo("model").getName()); //Zonda
System.out.println(car.getInfo("year").getName()); //2006
}
This ended up being my final solution which is a combination of MrLore and Sotirios Delimanolis solutions. This solution is completely dynamic without the use of any conditions.
This class performs the search for the name by passing in the property name;
String propertyName = "vehicleYear";
vehicleConfiguration.getInfo(propertyName).getName()
propertyName = "vehicleMake";
vehicleConfiguration.getInfo(propertyName).getName()
This class represents the VehicleConfiguration
#Entity
public class VehicleConfiguration extends StatefulEntity {
#ManyToOne
#JoinColumn(name = "year_id")
private VehicleYear vehicleYear;
#ManyToOne
#JoinColumn(name = "make_id")
private VehicleMake vehicleMake;
public LookupBaseEntity getInfo(final String fieldName) {
try {
String methodName = WordUtils.capitalize(fieldName);
Method method = VehicleConfiguration.class.getMethod("get" + methodName);
return (LookupBaseEntity) method.invoke(this);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(VehicleConfiguration.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
This class represents the VehicleYear
#Entity
public class VehicleYear extends LookupBaseEntity {
}
This class represents the VehicleMake
#Entity
public class VehicleMake extends LookupBaseEntity {
}
Which both extend LookupBaseEntity
public class LookupBaseEntity extends StatefulEntity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}