停止更新集合时,Hibernate没有更改 [英] Stop Hibernate from updating collections when they have not changed

查看:123
本文介绍了停止更新集合时,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 b $ b .get(com.xxx.MasterItem,id);

}

public void save(MasterItem master){
// master已经被UI改变了,因为它
getSessionFactory()。getCurrentSession ).saveOrUpdate(master);
}

当我加载一个MasterItem时,它会正确加载,并加载CriticalItems Set与数据,如指示。然后,我将此数据发送到我的UI,并获得更新的副本,然后我尝试持久。



当我调用save()方法时,Hibernate正在坚持为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:无法初始化代理 - 无会话;嵌套异常是org.hibernate.LazyInitializationException:无法初始化代理 - 无会话

我改回了saveOrUpdate()。



我终于找到了为什么 - 我没有在中包含 CascadeType.MERGE @Cascade 注释(ugh)。一旦我修正了,异常消失。

解决方案

这是语义上的区别 update / code>和 merge()






















$ b


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

...

如果项对象在传递给
update()之前或之后修改,则无关紧要。

...

Hibernate
总是将对象视为脏的,并计划一个SQL UPDATE。,它将在flush期间执行


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



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


I have two entity beans defined as follows (unrelated stuff removed):

@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 is defined as follows:

@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;
    }
}

And in my DAO code - I have these methods:

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);
}

When I load a MasterItem, it loads correctly, and also loads the CriticalItems Set with data, as directed. Then, I send this data to my UI, and get an updated copy back, which I then try to persist. The user updates fields in the MasterItem object, but does not touch the CriticalItems Set or anything in it - it remains unmodified.

When my save() method is invoked, Hibernate is insisting on sending SQL updates for each item in the Set of CriticalItems, even though none of them have changed in any way.

After some digging, here's what I think is happening. When I do a saveOrUpdate(), Hibernate sees my MasterItem object is in a detached state, so it tries to reload it from disk. However, when doing so, it appears to be using a prepared statement (which was auto-created by Hibernate at start-up) and this prepared statement does not attempt to join to the CriticalItems data.

So Hibernate has my updated MasterItem object with a full Set of CriticalItems, but uses a MasterItem without collections as its "previousState" object. Thus, all CriticalItems get updated via SQL (not inserted, which is interesting in itself).

Did I do something in my annotations that caused this behavior? I know I can use an Interceptor to find out of the item has really changed, or changed the dirty flag to override Hibernate's default algorithm - but this seems to be something that Hibernate should just handle on its own without my intervention.

Any insight would be appreciated.

UPDATE: Based on comments, I think I understand the difference between saveOrUpdate() and merge(). I realize that saveOrUpdate() will result in either an SQL INSERT or an SQL UPDATE in all cases, and that merge, in theory, will only issue updates if the object has changed from it's persistent state, but in order to determine that, Hibernate has to reload the object first via SQL SELECT.

So, I thought I could just go back into my code and change saveOrUpdate() to merge() and it would work, but that wasn't quite the case.

When I used merge(), I was getting

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

but it worked fine if I changed back to saveOrUpdate().

I finally found out why - I didn't include CascadeType.MERGE in my @Cascade annotation (ugh). Once I fixed that, the exception went away.

解决方案

That's the semantical difference between update() and merge().

From Christian Bauer and Gavin King's Java Persistence with Hibernate (I can't find clear explanation of this behaviour in the Hibernate docs):

The update() method forces an update to the persistent state of the object in the database, always scheduling an SQL UPDATE.
...
It doesn’t matter if the item object is modified before or after it’s passed to update().
...
Hibernate always treats the object as dirty and schedules an SQL UPDATE., which will be executed during flush.

On the other hand, merge() queries the database first, and doesn't perform update if state haven't changed.

So, if you want Hibernate to query the database first, you need to use merge() (though default behaviour of update() can be overriden by specifing @org.hibernate.annotations.Entity(selectBeforeUpdate = true) on your entities).

这篇关于停止更新集合时,Hibernate没有更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆