一尘不染

当收藏未更改时,停止Hibernate更新收藏

hibernate

我有两个定义如下的实体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”对象。因此,所有CriticalItems都通过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@Cascade注释中添加(ugh)。一旦我解决了,异常就消失了。


阅读 194

收藏
2020-06-20

共1个答案

一尘不染

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

摘自Christian Bauer和Gavin King的《
Hibernate的Java持久性》
(我在Hibernate文档中找不到对此行为的清晰解释):

update()方法强制更新到数据库中对象的持久状态,始终调度SQL UPDATE。

在将项目对象传递给update()之前或之后进行修改都没有关系。

Hibernate始终将对象视为脏对象,并计划将在刷新期间执行的SQL UPDATE.。

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

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

2020-06-20