Hibernate 的 saveOrUpdate 行为

42

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

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

5个回答

46
当你使用.saveOrUpdate()时,Hibernate会检查对象是否为临时的(它没有标识属性),如果是,则生成标识符并将其分配给会话以使其持久化。如果对象已经有了标识符,则执行.update()
根据文档saveOrUpdate()做以下几件事情:
  • 如果对象在此会话中已经是持久化的,那么什么都不做
  • 如果与会话相关联的另一个对象具有相同的标识符,则抛出异常
  • 如果对象没有标识符属性,则保存(save())它
  • 如果对象的标识符具有新实例化对象分配的值,则保存(save())它
  • 如果对象由“版本”或“时间戳”进行版本控制,并且版本属性值与新实例化对象分配的相同值,则保存(save())它;否则更新(update())该对象

1
如果对象的标识符具有分配给新实例化对象的值,则保存(save())。我创建了一个分离对象的clone()(保持相同的标识符),并修改了一个值。Hibernate update()该对象。它属于哪个类别? - iliaden
抱歉,我已经修正了我的答案并进行了正确的格式化。我猜在“如果与会话关联的另一个对象具有相同的标识符,则抛出异常”下面?! - Rihards
我没有收到任何异常,因为第一个对象通过session.evict()被分离了。 - iliaden

11
也许引用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

2

此处所述,saveOrUpdate方法可以通过生成新的标识符来保存瞬态实例或更新/重新关联与其当前标识符相关联的游离实例。更具体地说,它会执行以下操作:

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

1
如果有人在理论上不太理解,那么就需要代码。
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
}

1

根据主键的值完成此操作。如果主键未定义,则其值将默认为0,用于数字代理键并执行 保存 操作。如果填写了主键,则会调用 更新 操作。


这是不正确的,对我来说,即使我提供一个相同的主键更新,saveOrUpdate也不会更新。 - Siddharth
@Siddharth:没有看到你的代码,很难确定问题所在。我已经使用Hibernate 8年了,saveOrUpdate对我来说一直都很好用。 - Olaf
我只能在hbm中使用update = true、insert = false属性才能使其工作。 - Siddharth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接