我有两个如下定义的实体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
。一旦我解决了这个问题,那个例外就消失了。
答案 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;