Hibernate的saveOrUpdate行为

有谁知道Hibernate是如何知道是否INSERT或更新数据库中的一个值,当session.saveOrUpdate()被调用?

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

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

从文档 :

saveOrUpdate()执行以下操作:

  • 如果该对象已经在这个会话中持久化,则什么也不做
  • 如果与会话关联的另一个对象具有相同的标识符,则抛出exception
  • 如果对象没有标识符属性,则保存()它
  • 如果对象的标识符具有赋值给新实例化对象的值,则save()
  • 如果对象是由or定义的版本,并且版本属性值是分配给新实例化对象的相同值,则save()否则update()对象

也许有助于引用Hibernate的圣经( Java Persistence with Hibernate,第二版 ,第528页):

更有经验的Hibernate用户专门使用saveOrUpdate() ; 让Hibernate决定什么是新的和什么是旧的,特别是在一个更复杂的混合状态的对象networking中更容易。 saveOrUpdate()唯一的(不是真正的严重的)缺点是它有时候不能猜测一个实例是旧的还是新的,而不会在数据库触发一个SELECT – 例如,当一个类使用自然复合键映射时,没有版本或时间戳属性。

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

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

如此处所述, saveOrUpdate通过生成新的标识符来保存临时实例,或者更新/重新挂接与其当前标识符关联的分离实例。 更确切地说,它确实:

  • 如果该对象已经在这个会话中持久化,则什么也不做
  • 如果与会话关联的另一个对象具有相同的标识符,则抛出exception
  • 如果对象没有标识符属性,则save()
  • 如果对象的标识符具有赋值给新实例化对象的值,则save()
  • 如果对象由<version><timestamp>进行版本控制,并且版本属性值为
  • 将相同的值赋给一个新实例化的对象, save()
  • 否则update()该对象

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

  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 } 

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