Reputation: 45692
Question: How to check which fields has been changed inside method annotated with @PreUpdate
?
OPTIONAL: if the answer to the question above is "It's impossible, than maybe there are another ways to solve my problem"
I want automatically update modified
Tourist
's field each time we change something in it.
Except the situation when we modify only location
. Means if we change location
only - it should be persisted, but modified
mustn't be changed.
Already present code:
@Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
@PreUpdate
public void preUpdate() {
modified = new Date(); //PROBLEM : change modified even if only location field has been changed!
}
....
}
Updated: After some investigations I found that I can solve it with help of interceptors (extend EmptyInterceptor
):
public class TouristInterceptor extends EmptyInterceptor{
Session session;
private Set updates = new HashSet();
public void setSession(Session session) {
this.session=session;
}
public boolean onFlushDirty(Object entity,Serializable id,
Object[] currentState,Object[] previousState,
String[] propertyNames,Type[] types)
throws CallbackException {
if (entity instanceof Tourist){
if (somethingChangedExceptLocation())
updates.add(entity);
}
return false;
}
But disadvantage of this approach is to intercept everything when you need to intercept the single entity.
Updated Questions:
PreUpdateEvent
which contains new and old stateUpvotes: 4
Views: 16414
Reputation: 23226
There is a simple non-JPA solution which is as follows but which which does have some repetitive code but is a solution when you do not have too many fields:
@Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
public void setFirstName(String firstName){
if(! this.firstName.equals(firstName){
modified = new Date();
}
this.firstName = firstName;
}
public void setLastName(String lastName){
if(! this.lastName.equals(lastName){
modified = new Date();
}
this.lastName= lastName;
}
}
Otherwise I would go with saving the previous state on load as suggested in another answer but in a slightly cleaner way.
@Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
@Transient
private Tourist previousState;
@PostLoad
public void setPreviousState() {
previousState = new Tourist();
//copy fields
}
@PreUpdate
public void preUpdate() {
if (isModified()) {
modified = new Date();
}
}
private boolean isModified() {
boolean modified = false;
if (!firstName.equals(previousState.firstName) {
modified = true;
}
//check other fields
return modified;
}
}
Upvotes: 5
Reputation: 1724
Although it is much late, however I enountered a problem where I required to apply update security on certain fields in entity. I solved it using Reflections. Have a look at this thread. spring security for certain entity variables
Upvotes: 0
Reputation: 636
I really encourage you not to do this logic in your Entity
!
You should decide whether to change the modified
or not at your business logic.
I mean, when you are changing only the location call merge
only. And whenever you are changing something else call the <Tourist_instance>.setModified(new Date());
before you call the merge
.
To verify if anything else got changed, I suggest having a transient boolean
field in your Entity
that you set to true
whenever you change something else other than location (the idea in the comment won't be sufficient to test all the fields, only if you create a transient field to remember previous value of each one of them)
You will then test this boolean
in your preUpdate method
But I highly don't recommend this workaround.
Or, another highly unrecommended way :
@Entity
public class Tourist {
private long id;
private String firstName;
private String lastName;
private Date created;
private Date modified;
private String location;
@Transient
private String firstNamePrevious;
@Transient
private String lastNamePrevious;
@Transient
private Date createdPrevious;
@Transient
private Date modifiedPrevious;
@Transient
private String locationPrevious;
@PreUpdate
public void preUpdate() {
if(!onlyLocationGotChanged()){
modified = new Date();
}
}
....
@PostLoad
public void postLoad(){
//Set all previous fields to actual values
firstNamePrevious = firstName;
lastNamePrevious = lastName;
//...etc.
}
}
Upvotes: -1