休眠保存或更新行为

Posted

技术标签:

【中文标题】休眠保存或更新行为【英文标题】:Hibernate saveOrUpdate behavior 【发布时间】:2011-09-20 13:58:55 【问题描述】:

有谁知道 Hibernate 在调用 session.saveOrUpdate() 时如何知道是插入还是更新数据库中的值?

到目前为止,我只确定它不依赖于缓存中的信息,数据库中实体的存在是由主键决定的。

【问题讨论】:

【参考方案1】:

当您使用.saveOrUpdate() 时,Hibernate 将检查对象是否是瞬态的(它没有标识符属性),如果是,它将通过生成标识符并将其分配给会话来使其持久化。如果对象已经有标识符,它将执行.update()

来自documentation:

saveOrUpdate() 执行以下操作:

如果对象已经持久化 在此会话中,什么都不做 如果另一个对象与 会话具有相同的标识符, 抛出异常 如果对象没有标识符 属性,保存()它 如果对象的标识符具有 分配给新的值 实例化对象,保存()它 如果对象由 “版本”或“时间戳”,以及 版本属性值相同 分配给新的值 实例化对象,保存()它 否则更新()对象

【讨论】:

"如果对象的标识符具有分配给新实例化对象的值,则 save()" 我创建了一个分离对象的clone()(保持相同的标识符),并修改了一个值。休眠update() 对象。它属于哪个类别? 对不起,用正确的格式修正了我的答案。我猜在“如果与会话关联的另一个对象具有相同的标识符,则抛出异常”?! 我没有得到任何异常,因为第一个对象是通过session.evict()分离的。【参考方案2】:

如果有人在理论上没有真正理解,那么有一个代码

MyModel sent = myDao.myDaoImpl(id); 

if(sent == null)                        
    sent = **new MyModel();** // new Object
    sent.setXX(id);
    sent.setYY("Yes");
    sent.setDate(new Date());
    myDao.saveOrUpdate(sent); // Insert will be called
 else if(! "Yes".equalsIgnoreCase(sent.getFlag())) 
    sent.setXX("Yes");
    sent.setDate(new Date());
    myDao.saveOrUpdate(sent); // Update will be called

【讨论】:

【参考方案3】:

也许引用 Hibernate 圣经会有所帮助(Java Persistence with Hibernate, 2nd ed.,第 528 页):

更有经验的 Hibernate 用户只使用saveOrUpdate();让 Hibernate 决定什么是新的和什么是旧的要容易得多,尤其是在具有混合状态的更复杂的对象网络中。独占saveOrUpdate() 唯一(不是很严重)的缺点是它有时无法猜测实例是旧的还是新的而不在数据库中触发SELECT - 例如,当一个类被映射到一个自然复合时键,没有版本或时间戳属性。

Hibernate 如何检测哪些实例是旧的,哪些是新的?有一系列选项可供选择。 Hibernate 假设一个实例是一个未保存的瞬态实例,如果:

标识符属性为null。 版本或时间戳属性(如果存在)是null。 由 Hibernate 在内部创建的同一持久类的新实例具有与给定实例相同的数据库标识符值。 您在类的映射文档中提供unsaved-value,并且标识符属性的值匹配。 unsaved-value 属性也可用于版本和时间戳映射元素。 具有相同标识符值的实体数据不在二级缓存中。 在检查代码中的实例后,您提供一个实现或org.hibernate.Interceptor 并从Interceptor.isUnsaved() 返回Boolean.TRUE

【讨论】:

【参考方案4】:

这是根据主键的值完成的。如果未定义主键,则数字代理键的值将默认为 0,并且将执行 save。如果填写了主键,则会调用update

【讨论】:

这是不正确的,对我来说,即使我提供具有相同主键的更新,saveOrUpdate 也不会更新。 @Siddharth:如果没有看到代码,很难说出代码中的问题。我使用 Hibernate 已经 8 年了,saveOrUpdate 对我来说效果很好。 我只能使用 hbm 中的 update = true,insert = false 属性。【参考方案5】:

如here 所述,saveOrUpdate 要么通过生成新标识符来保存临时实例,要么更新/重新附加与其当前标识符关联的分离实例。更具体地说:

如果对象已在此会话中持久存在,则不执行任何操作 如果与会话关联的另一个对象具有相同的标识符,则抛出异常 如果对象没有标识符属性,save()它 如果对象的标识符具有分配给新实例化对象的值,则保存()它 如果对象由<version><timestamp> 进行版本控制,并且版本属性值为 分配给新实例化对象的相同值,save() it 否则update()对象

【讨论】:

以上是关于休眠保存或更新行为的主要内容,如果未能解决你的问题,请参考以下文章

更新期间休眠不刷新

更新不起作用时 Laravel 保存(奇怪的行为)

JOOQ 与休眠行为

带有休眠和触发器的乐观锁定 - “奇怪”行为

Linux上的pthread条件变量,奇怪的行为

休眠删除查询