Getting object field previous value hibernate JPA - java

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;
}
}

Related

Using class as extend of the Entity, but it is not the physical table in the model

I am not sure if title of the post is understandable, but I will try to explain it with examples.
The main class is the entity, lets name it Animal
#Entity
public class Animal {
#Id
private Long id;
private String param1;
}
The param1 is some basic property which only extended classes will expose what it represents:
public class Cat extends Animal {
public String getName() {
return super.getParam1();
}
public void setName(String name) {
super.setParam1(name);
}
}
public class Dog extends Animal {
public String getBreed() {
return super.getParam1();
}
public void setBreed(String breed) {
super.setParam1(breed);
}
}
When creating a new object of Cat and trying to save it, I get the error Unknown entity: com.example.Cat.
I have read about persistence inheritance but I think this is not the case since mine extended classes are just some logic in the services and not the actual tables in the model.
Is there any solution on how I can save the superclass ?

How to get a class object in #PrePersist method

This is my class
public class Customer{
private String programId;
private String entityName;
private String columnName;
#PrePersist
doSomething(){
doSomeService.newMethod(//how can I pass class object here with all its values);
}
}
I want to access few attributes of class in my prepersist to do some logic , is it possible to get class object with its values inside PrePersist method
You can use it like below
#PrePersist
private void beforeSave( Customer customer )
{
}

"No setter/field for field found on class"

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.

facing issue with unmarshalling an old xml ,after class structure got changed

I am struggling with unmarshalling an old xml file whose structure is different than my current object structure.
Previous structure
#xmlRootElement("configData")
public class configData{
private string name;
private string age;
private customObject obj;
}
My current data structure is
#xmlRootElement("configData")
public class configData{
List<SampleData> sampleDatas;;
}
public class SampleData{
private string name;
private string age;
private customObject obj;
}
How to make it work with old xml file. Please help.
Thanks
Your old structure suggest, that only one set of SampleData exists in the XML file.
So You should try something like this:
#XmlRootElement
public class ConfigData
{
// This will hide the list from JAXB
#XmlTransient
private final List<SampleData> sampleDatas = new ArrayList<>();
private SampleData getFirstSample()
{
if(sampleDatas.isEmpty())
sampleDatas.add(new SampleData());
return sampleDatas.get(0);
}
// Façade methods to delegate functionality to the list's first item...
// Only setters are required, if you just want to read in an old format.
// However this would not be optional, if you want to save to the new format...
public void setName(String name)
{
getFirstSample().setName(name);
}
public void seAge(String age)
{
getFirstSample().setAge(age);
}
public void setObj(CustomObject obj)
{
getFirstSample().setObj(obj);
}
}
public class SampleData
{
private String name;
private String age;
private CustomObject obj;
// Accessor methods...
}
The façade setter methods in ConfigData store thier values to the List's first item.
To provide the possibility to save, you should remove the #XmlTransient, and provide public getters to the fields you want to save...

How to set validation context with annotations

Imagine I've an action MyAction, with a field User with getters and setters.
Then I have two exposed public methods where I want to use visitor validation for the object User but with two different contexts, so that the User is validated in two different ways, depending on the method that has been called.
I want to do this with annotations only, no validation.xml.
See the example
public class MyAction extends ActionSupport {
private User user;
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user=user;
}
public string execute() {
//some code here ...
return SUCCESS;
}
#Validations(
visitorFields = {#VisitorFieldValidator(context="myContext1", fieldName="user", key="error.message", message="A default error message", shortCircuit=true)}
)
public string save() {
//some code here ...
return SUCCESS;
}
#Validations(
visitorFields = {#VisitorFieldValidator(context="myContext2", fieldName="user", key="error.message", message="A default error message", shortCircuit=true)}
)
public string update() {
//some code here ...
return SUCCESS;
}
//...
}
Now I'd like to specify the context (myContext1 and myContext2) into the annotations inside the User object, so that some validators fire when the save() method is called on MyAction while others fire when update() is called.
I imagine something like this:
public class User implements Serializable {
private Integer id;
private String name;
#RequiredFieldValidator(context="myContext2", message = "You must enter a value for data.")
public void setId(Integer id) {
this.id=id;
}
#RequiredFieldValidator(context="myContext1", message = "You must enter a value for data.")
public void setName(String name) {
this.name = name;
}
//some code... getters, etc...
}
I'd like to do this so that, for example, during creation operation (method save()) the name is required while during update operation (method update() ) the id is required. Unfortunately it seems to me that RequiredFieldValidator, so any other validator annotation in Struts2, does not support the "context" field.
I know that I can use a User-myContext1-validation.xml, and a User-myContext2-validation.xml file (I've not tried but I think I've understood it can be done) but I'd like to use annotations only.
Do you know How I can do this with annotations?

Categories