问题: 如何检查在使用@PreUpdate
注释的方法中哪些字段已更改?
可选:如果上述问题的答案是"这是不可能的,也许还有其他方法可以解决我的问题"
每次我们更改内容时, 我想自动更新modified
Tourist
字段。
location
时, 除了情况。表示我们只更改location
- 应该保留,但modified
不得更改。
已有代码:
@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!
}
....
}
更新:经过一些调查,我发现我可以在拦截器的帮助下解决它(扩展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;
}
但这种方法的缺点是在需要拦截单个实体时拦截所有内容。
更新了问题:
PreUpdateEvent
答案 0 :(得分:1)
有一个简单的非JPA解决方案如下,但它确实有一些重复的代码,但是当你没有太多的字段时它是一个解决方案:
@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;
}
}
否则我会按照另一个答案的建议保存前一个加载状态,但方式稍微更清晰。
@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;
}
}
答案 1 :(得分:0)
我真的鼓励你不要在你的Entity
中执行此逻辑!
您应该根据业务逻辑决定是否更改modified
。
我的意思是,当您仅更改位置调用merge
时。在您更改其他内容时,请在致电<Tourist_instance>.setModified(new Date());
之前致电merge
。
为了验证是否有其他更改,我建议您在boolean
中设置一个短暂的Entity
字段,只要您更改了位置以外的其他内容,您就设置为true
只有当你创建一个瞬态字段来记住每个字段的前一个值时,评论才足以测试所有字段。
然后,您将在boolean
preUpdate method
进行测试
但我非常不推荐这种解决方法。
或者,另一种非常不受推荐的方式:
@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.
}
}
答案 2 :(得分:0)
虽然现在已经很晚了,但是我遇到了一个问题,我需要在实体的某些字段上应用更新安全性。我用反射解决了它。看看这个帖子。 spring security for certain entity variables