站在持久化的角度, Hibernate 把对象分为 4 种状态: 持久化状态, 临时状态, 游离状态, 删除状态. Session 的特定方法能使对象从一个状态转换到另一个状态.
持久化对象的状态
临时对象(Transient):
–在使用代理主键的情况下 , OID 通常为 null– 不处于 Session 的缓存中– 在数据库中没有对应的记录
持久化对象(也叫”托管”)(Persist):
– OID 不为 null– 位于 Session 缓存中–若在数据库中已经有和其对应的记录, 持久化对象和数据库中的相关记录对应– Session 在 flush 缓存时 , 会根据持久化对象的属性变化 , 来同步更新数据库– 在同一个 Session 实例的缓存中 , 数据库表中的每条记录只对应唯一的持久化对象
删除对象(Removed)
游离对象(也叫”脱管”) (Detached):
–在数据库中没有和其 OID 对应的记录–不再处于 Session缓存中–一般情况下, 应用程序不该再使用被删除的对象
OID 不为 null
不再处于 Session 缓存中
一般情况需下, 游离对象是由持久化对象转变过来的, 因此在数据库中可能还存在与它对应的记录
对象的状态转换图
save()与persist()
- Session 的 save() 方法使一个临时对象转变为持久化对象
-
Session 的 save() 方法完成以下操作:
– 把 News 对象加入到 Session 缓存中 , 使它进入持久化状态– 选用映射文件指定的标识符生成器 , 为持久化对象分配唯一的 OID .在使用代理主键的情况下, setId()方法为News对象设置OID是无效的.数据库会自动重新分配id– 计划执行一条 insert 语句:在 flush 缓存的时候
-
Hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系. 当 News 对象处于持久化状态时,不允许程序随意修改它的 ID
-
persist() 和 save() 区别(persist()也会执行insert操作):
–当对一个 OID 不为Null的对象执行save()方法时,会把该对象以一个新的oid保存到数据库中; 但执行 persist()方法时会抛出一个异常.(在调用persist方法之前,若对象已经有id了,则不会执行insert,,而抛出一个异常)
get()与load()
- 都可以根据跟定的 OID 从数据库中加载一个持久化对象
- 执行load方法,若不使用该对象,则不会立即执行查询操作,而返回一个代理对象
-
区别:
–当数据库中不存在与OID对应的记录时,且session也没有被关闭,load()若不使用该对象没问题,若需要初始化了,会抛出 ObjectNotFoundException异常,而get()方法返回null–两者采用不同的 延迟检索策略: load 方法支持延迟加载策略。而 get 不支持。–load()可能会抛出LazyInitializationException 异常:在需要初始化代理对象之前已经关闭了session
update()
- 若更新一个持久化对象,不需要显示地调用update方法,因为在调用Transaction的commit()方法时,会先执行session的flush方法
- Session 的 update() 方法使一个游离对象转变为持久化对象, 并且计划执行一条 update 语句.
- 注意:
1.无论要更新的游离对象和数据库的记录是否一致,都会发送update语句如何能让update方法不盲目的触发update语句呢? 若希望 Session 仅当修改了 News 对象的属性时, 才执行 update() 语句, 可以把映射文件中 <class> 元素的 select-before-update 设为 true. 该属性的默认值为 false,但通常不需要设置该设置该属性(在update的情况下需要多发一条select语句)2. 当 update() 方法关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常.3. 当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常。因为在session缓存中不能有两个oid相同的对象。
saveOrUpdate()
- Session 的 saveOrUpdate() 方法同时包含了 save() 与 update() 方法的功能
-
判定对象为临时对象的标准
– Java 对象的 OID 为 null–映射文件中为<id>设置了 unsaved-value 属性,并且Java对象的OID取值与这个unsaved-value属性值匹配 -
注意
1.若OID不为null,但数据表中还没有和其对应的记录,会抛出一个异常2.了解:OID值等于id的unsaved-value属性的对象也被认为是一个游离对象
merge()
delete()
- 执行删除操作,只要OID和数据表中一条记录对应,就会准备执行delete操作,若OID在数据表中没有对应的记录,则抛出异常
- Session 的 delete() 方法既可以删除一个游离对象, 也可以删除一个持久化对象
-
Session的delete()方法处理过程–计划执行一条delete语句(提交之前该对象还是具有OID的,还是可以使用该对象)–提交之后把对象从Session缓存中删除,该对象进入删除状态.
-
Hibernate的cfg.xml配置文件中有一个hibernate.use_identifier_rollback属性,其默认值为false,若把它设为true,将改变delete()方法的运行行为:delete() 方法会把持久化对象或游离对象的 OID设置为null,使它们变为临时对象
evict()
- 从session缓存中把指定的持久化对象移除
通过 Hibernate 调用存储过程
Work 接口: 直接通过 JDBC API 来访问数据库的操作
Session 的 doWork(Work) 方法用于执行
Work 对象指定的操作, 即调用 Work 对象的 execute() 方法. Session 会把当前使用的数据库连接传递给 execute() 方法.
Hibernate 与触发器协同工作
Hibernate 与数据库中的触发器协同工作时, 会造成两类问题
–触发器使Session的缓存中的持久化对象与数据库中对应的数据不一致:触发器运行在数据库中,它执行的操作对Session是透明的–Session的update()方法盲目地激发触发器:无论游离对象的属性是否发生变化,都会执行update语句,而update语句会激发数据库中相应的触发器
解决方案:
–在执行完Session的相关操作后,立即调用Session的flush()和refresh()方法,迫使Session的缓存与数据库同步(refresh()方法重新从数据库中加载对象)
-在映射文件的的 <class> 元素中设置 select-before-update 属性: 当 Session 的 update 或 saveOrUpdate() 方法更新一个游离对象时, 会先执行 Select 语句, 获得当前游离对象在数据库中的最新数据, 只有在不一致的情况下才会执行 update 语句