当Hibernate没有更改时,阻止Hibernate更新集合

时间:2011-01-18 15:19:28

标签: hibernate collections persist

我有两个如下定义的实体bean(删除了不相关的东西):

@Entity
@Table(...)
public class MasterItem implements java.io.Serializable {

  private Set<CriticalItems> criticalItemses = new HashSet<CriticalItems>(0);

  @OneToMany(fetch = FetchType.EAGER, mappedBy = "masterItem", orphanRemoval = true,
            cascade = {javax.persistence.CascadeType.DETACH})
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
    public Set<CriticalItems> getCriticalItemses() {
        return this.criticalItemses;
    }
}

CriticalItems定义如下:

@Entity
@Table(...)
public class CriticalItems implements java.io.Serializable {

    private MasterItem masterItem;

    @ManyToOne(fetch = FetchType.LAZY, optional = false,
            cascade = {javax.persistence.CascadeType.DETACH})
    @Cascade({CascadeType.SAVE_UPDATE})
    @JoinColumn(name = "mi_item_id", nullable = false)
    public MasterItem getMasterItem() {
        return this.masterItem;
    }
}

在我的DAO代码中 - 我有这些方法:

public MasterItem load(int id) {
    MasterItem results = (MasterItem) getSessionFactory().getCurrentSession()
        .get("com.xxx.MasterItem", id);

}

public void save(MasterItem master) {
    // master has been changed by the UI since it
    getSessionFactory().getCurrentSession().saveOrUpdate(master);
}

当我加载MasterItem时,它会正确加载,并按照指示加载带有数据的CriticalItems Set。然后,我将这些数据发送到我的UI,并获得更新的副本,然后我尝试继续。用户更新MasterItem对象中的字段,但不会触及CriticalItems Set或其中的任何内容 - 它仍未修改。

当调用我的save()方法时,Hibernate坚持为Set of CriticalItems中的每个项发送SQL更新,即使它们都没有以任何方式发生变化。

经过一番挖掘,这就是我认为正在发生的事情。当我执行saveOrUpdate()时,Hibernate看到我的MasterItem对象处于分离状态,因此它尝试从磁盘重新加载它。但是,在执行此操作时,它似乎正在使用预准备语句(由Hibernate在启动时自动创建),并且此预准备语句不会尝试连接到CriticalItems数据。

因此,Hibernate拥有我更新的MasterItem对象,其中包含一整套CriticalItems,但使用不带集合的MasterItem作为其“previousState”对象。因此,所有CriticalItem都通过SQL更新(未插入,这本身就很有趣)。

我在注释中做了哪些导致这种行为的事情?我知道我可以使用拦截器找出该项已经真正改变,或者更改了脏标志以覆盖Hibernate的默认算法 - 但这似乎是Hibernate应该在没有我干预的情况下自行处理的事情。

任何见解都将受到赞赏。

更新 根据评论,我想我理解saveOrUpdate()和merge()之间的区别。我意识到saveOrUpdate()在所有情况下都会导致SQL INSERT或SQL UPDATE,理论上,合并只会在对象从其持久状态发生变化时才发出更新,但为了确定,Hibernate必须首先通过SQL SELECT重新加载对象。

所以,我想我可以回到我的代码并将saveOrUpdate()更改为merge(),它会起作用,但情况并非如此。

当我使用merge()时,我正在

org.springframework.orm.hibernate3.HibernateSystemException: could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session

但如果我改回saveOrUpdate(),它工作正常。

我终于找到了原因 - 我没有在CascadeType.MERGE注释(ugh)中包含@Cascade。一旦我解决了这个问题,那个例外就消失了。

2 个答案:

答案 0 :(得分:16)

这是update()merge()之间的语义差异。

来自Christian Bauer and Gavin King's Java Persistence with Hibernate(我在Hibernate文档中找不到这种行为的明确解释):

  

update()方法强制更新对象的持久状态   数据库,始终安排SQL UPDATE   ...
  项目对象在传递之前或之后是否被修改无关紧要   更新()。
  ...
  过冬   始终将对象视为脏对象并计划将执行的SQL UPDATE   在冲洗期间。

另一方面,merge()首先查询数据库,如果状态没有改变,则不执行更新。

因此,如果您希望Hibernate首先查询数据库,则需要使用merge()(尽管可以通过在您的实体上指定update()来覆盖@org.hibernate.annotations.Entity(selectBeforeUpdate = true)的默认行为。 / p>

答案 1 :(得分:1)

尝试向您的实体添加修订列(乐观锁定)

@Version
Date lastModified;