获取对象字段先前值hibernate JPA

问题描述 投票:0回答:1

假设我有这门课:

@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
方法而不是我刚刚更新的方法从数据库获取值?

java hibernate caching jpa annotations
1个回答
31
投票

我认为一个简单的方法是将先前的值保存在 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 假定实体侦听器没有状态,因此它可以随时创建新实例并按照自己喜欢的方式使用它们。 因此,将实体实例特定状态放入侦听器实例中是一个坏主意。

最后,如果您还想访问实体中的先前状态,则您没有机会将其存储在侦听器中。

© www.soinside.com 2019 - 2024. All rights reserved.