假设我有这门课:
@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);
}
}
现在在我的
MyListener
类中,我试图找出之前的值 MyClass
实例与即将保存(更新)到数据库的新值。我用预更新方法来做到这一点:
@PreUpdate
public void preUpdate(Object entity) {
...some logic here...unable to get the old object value in here
}
所以假设我有一个带有名字和姓氏的
MyClass
对象实例:
MyClass mycls = new MyClass();
mycls.setName("Bob");
mycls.setSurname("Dylan");
mycls.save();
这没有被监听器接收到,这没关系,因为我只监听更新。 现在,如果我像这样更新这个实例:
MyClass cls = MyClass.findById(someLongId)
cls.setSurname("Marley");
cls.update();
所以这会触发我的监听器中的
preUpdate
方法,当我尝试时:
MyClass.findById(someLongId);
当我调试时,我已经获得了新更新的实例,但更新尚未发生,因为当我在姓氏列中签入数据库时,它仍然是
Dylan
。
如何使用我的
preUpdate
方法而不是我刚刚更新的方法从数据库获取值?
我认为一个简单的方法是将先前的值保存在 JPA 不会持久的瞬态变量中。 因此,只需引入一个变量 previousSurname 并保存实际值,然后再在 setter 中覆盖它即可。
如果你想保存多个属性,如果你的类
MyClass
是Serializable.
,那就很容易了
如果是这样,添加一个加载后监听器
public class MyClass implements Serializable {
@Transient
private transient MyClass savedState;
@PostLoad
private void saveState(){
this.savedState = SerializationUtils.clone(this); // from apache commons-lang
}
}
但请注意,
savedState
是一个独立的实例。
然后您可以在
EntityListener
中访问之前的状态。
您还可以将
PostLoad
侦听器移至 EntityListener
类。但随后您需要访问 savedState
字段。我建议将其设置为包范围或使用包范围访问器并将 MyClass
和 MyListener
放在同一个包中,
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;
}
}
编辑
为什么不在加载后期间将保存的状态放入侦听器中?这样你就不需要访问 MyClass 的属性
首先,侦听器类型是为特定类而不是对象注册的
@EntityListeners(class=MyListener.class)
所以,如果hibernate创建了一个新的监听器实例,你不能确定同一个监听器实例用于同一个目标对象。 hibernate 文档 指出:
实体侦听器是具有无参数构造函数的无状态类。
由于 hibernate 假定实体侦听器没有状态,因此它可以随时创建新实例并按照自己喜欢的方式使用它们。 因此,将实体实例特定状态放入侦听器实例中是一个坏主意。
最后,如果您还想访问实体中的先前状态,则您没有机会将其存储在侦听器中。