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
。