Hibernate @PreUpdate:检查已更改的内容
Posted
技术标签:
【中文标题】Hibernate @PreUpdate:检查已更改的内容【英文标题】:Hibernate @PreUpdate: check what has been changed 【发布时间】:2015-02-17 08:12:18 【问题描述】:问题: 如何检查带有@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
包含新旧状态
【问题讨论】:
为什么不使用临时字段来存储以前的位置,并在更新前测试它,然后再更新modified
字段?
我建议您创建一个边类,您可以在其中跟踪事件(创建、修改等),并在业务级别而不是 DAO 级别更新它
@Hichamov 我真的很喜欢你关于额外瞬态场的想法!
@Hichamov 但我需要检查其他字段是否未更改。请详细说明模式
对不起,我以前从未使用过。我可能说错了
【参考方案1】:
我真的鼓励你不要在你的Entity
中这样做!
您应该根据您的业务逻辑决定是否更改modified
。
我的意思是,当您仅更改位置时,仅调用 merge
。每当您更改其他内容时,请先致电 <Tourist_instance>.setModified(new Date());
,然后再致电 merge
。
要验证是否有其他任何更改,我建议在您更改位置以外的其他内容时在您的Entity
中设置一个临时的boolean
字段,将其设置为true
(评论中的想法不会是足以测试所有字段,前提是您创建一个临时字段来记住每个字段的先前值)
然后您将在您的preUpdate method
中测试此boolean
但我强烈不推荐这种解决方法。
或者,另一种非常不推荐的方式:
@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.
【讨论】:
你是对的,但应用程序太大了,我无法确定所有用途。或者强迫大家以正确的方式保存Tourist
实体。所以我正在寻找一些通用的方法。你觉得 EmtpyInterceptor#onFlushDirty ***.com/questions/1927098/… 怎么样?
在业务逻辑中做的提议未能封装操作。然后这个类的每个客户都需要知道它还应该更新修改日期,这是非常糟糕的!
我真的不明白它是什么,因为我从未使用过它。但是Object[] newValues
和Object[] oldValues
似乎很方便
@AlanHay 我完全同意(我支持你),这就是我保留其他建议的原因。我只是想指出它以防 V_B 可行
还要注意这里的transient关键字在JPA环境下是没有作用的。您正在寻找的是 @Transient 注释。【参考方案2】:
有一个简单的非 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;
【讨论】:
我有大约二十个字段((. 您可以查看使用 BeanUtils 在加载时将数据复制到瞬态实例并在保存时比较两个 bean:***.com/questions/6099040/… 您能看看我的问题的更新部分吗?我有一些想法 我不再担心二十个字段,而是开始输入它们。 @VB_【参考方案3】:虽然已经很晚了,但是我遇到了一个问题,我需要在实体中的某些字段上应用更新安全性。我使用反射解决了它。看看这个线程。 spring security for certain entity variables
【讨论】:
以上是关于Hibernate @PreUpdate:检查已更改的内容的主要内容,如果未能解决你的问题,请参考以下文章
所有警报对话框消息和textField都已更改为单行。请检查图像
使用 SessionFactory 配置 Hibernate 4.3 以使用 @PrePersist
@PrePersist 运行时,@PreUpdate 是不是总是运行?