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

Gor*_*don 10 collections hibernate 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的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);
}
Run Code Online (Sandbox Code Playgroud)

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

当我的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
Run Code Online (Sandbox Code Playgroud)

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

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

axt*_*avt 17

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

来自Christian Bauer和Gavin King的Java Persistence with Hibernate(我在Hibernate文档中找不到这种行为的清晰解释):

update()方法强制更新数据库中对象的持久状态,始终调度SQL UPDATE.
...
如果在将对象传递给update()之前或之后修改了它,则无关紧要.
...
Hibernate总是将对象视为脏对象并安排SQL UPDATE.,它将在刷新期间执行.

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

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