Hibernate @PreUpdate:检查已更改的内容

时间:2014-12-18 14:00:11

标签: java hibernate

问题: 如何检查在使用@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

3 个答案:

答案 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