提问者:小点点

如何在@HandleBeforeSave事件中获取旧实体值以确定属性是否已更改?


我正在尝试在@HandleBeforeSave事件中获取旧实体。

@Component
@RepositoryEventHandler(Customer.class)
public class CustomerEventHandler {

    private CustomerRepository customerRepository;

    @Autowired
    public CustomerEventHandler(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    @HandleBeforeSave
    public void handleBeforeSave(Customer customer) {
        System.out.println("handleBeforeSave :: customer.id = " + customer.getId());
        System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());

        Customer old = customerRepository.findOne(customer.getId());
        System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());
        System.out.println("handleBeforeSave :: old customer.name = " + old.getName());
    }
}

如果我尝试使用findOne方法获取旧实体,但这会返回新事件。可能是因为当前会话中的Hibernate/Repository缓存。

有办法获取旧实体吗?

我需要它来确定给定的属性是否被更改。如果属性发生更改,我需要执行一些操作。


共3个答案

匿名用户

如果使用Hibernate,您可以简单地将新版本从会话中分离出来并加载旧版本:

@RepositoryEventHandler 
@Component
public class PersonEventHandler {

  @PersistenceContext
  private EntityManager entityManager;

  @HandleBeforeSave
  public void handlePersonSave(Person newPerson) {
      entityManager.detach(newPerson);
      Person currentPerson = personRepository.findOne(newPerson.getId());
      if (!newPerson.getName().equals(currentPerson.getName)) {
          //react on name change
      }
   }
}

匿名用户

感谢Marcel Overdijk,创建门票-

我看到了这个问题的其他解决方法,并且也想贡献我的解决方法,因为我认为它很容易实现。

首先,在您的域模型(例如帐户)中设置一个瞬态标志:

@JsonIgnore
@Transient
private boolean passwordReset;

@JsonIgnore
public boolean isPasswordReset() {
    return passwordReset;
}

@JsonProperty
public void setPasswordReset(boolean passwordReset) {
    this.passwordReset = passwordReset;
}

其次,检查EventHandler中的标志:

@Component
@RepositoryEventHandler
public class AccountRepositoryEventHandler {

    @Resource
    private PasswordEncoder passwordEncoder;

    @HandleBeforeSave
    public void onResetPassword(Account account) {
        if (account.isPasswordReset()) {
            account.setPassword(encodePassword(account.getPassword()));
        }
    }

    private String encodePassword(String plainPassword) {
        return passwordEncoder.encode(plainPassword);
    }

}

注意:对于这个解决方案,您需要额外发送一个resetPassword=true参数!

对我来说,我正在向我的资源终结点发送一个HTTPPATCH,其中包含以下请求有效负载:

{
    "passwordReset": true,
    "password": "someNewSecurePassword"
}

匿名用户

您目前正在Hibernate上使用spring-data抽象。如果查找返回新值,则spring-data显然已经将对象附加到Hibernate会话。

我想你有三个选择:

  1. 在当前季节刷新之前,在单独的会话/事务中获取对象。这很尴尬,需要非常微妙的配置。
  2. 在Spring附加新对象之前获取以前的版本。这是非常可行的。您可以在将对象交给存储库之前在服务层执行此操作。但是,当另一个对象感染我们已知的相同类型和id时,您不能将对象保存在Hibernate会话中。在这种情况下,使用merge驱逐
  3. 使用这里描述的较低级别的Hibernate拦截器。正如你所看到的onFlushDirty有两个值作为参数。但是请注意,Hibernate通常不会查询你以前的状态,只是保存一个已经持久化的实体。取而代之的是在数据库中发出一个简单的更新(没有选择)。您可以通过在您的实体上配置选择前更新来强制选择。