复习 - Hibernate的持久化环境
Hibernate的持久化环境
很久没有使用Hibernate了,现在要复习一下, 面试的话这个是必不可少的.
1.Hibernate Session.
session的概念大概是这样:
(1). Session是一块内存,里面存放了Persistent状态的对象, 也就是Persistent Context(持久化环境)
(2). Session里面有一组排着队等着执行的Sql,这些sql的目的本质上都是为了确保缓冲区(session级别的缓存,一级缓存)的内容和数据库保持一致.
关于Persistent Context,注意几点:
(1). 就是存放对象的一块缓存
(2). 你不能通过相关的API来访问它(它就是隐藏在背后的一个神秘人物)
(3). 很明显,一个Hibernate Session对应一个Persistent Context
(4). 对应的是一级缓存,不是可选的,是必须的,而且不能disable.
除了效率的提高(缓存), 缓存还可以给我们带来另外的好处:
(1) 自动脏数据检查.
来个例子:
package com.yxy.test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.stat.Statistics; import com.yxy.bean.Student; public class HibernateStatTest { public static void main(String[] args){ SessionFactory sessionFactory = HibernateUtils.getSessionFactory(); Statistics stat = sessionFactory.getStatistics(); Session session = sessionFactory.openSession(); session.getTransaction().begin(); stat.setStatisticsEnabled(true); stat.clear(); Student student = (Student)session.load(Student.class, 1); student.setName("Hello Hibernate !"); System.out.println(stat.getEntityLoadCount()); stat.setStatisticsEnabled(false); session.getTransaction().commit(); session.close(); } }
并没有调用update方法去更新,但是在事务提交之后是可以自动的将更新同步到数据库中的,控制台中也发出了相关的update语句
Hibernate: /* update com.yxy.bean.Student */ update student set name=?, sex=?, register_date=? where id=?
原理很简单:
Hibernate会在session中保存一份对象最近从数据库中加载上来的快照(snapshot), 所以在写回到数据库中的时候(比如事务提交),会将当前的对象同快照进行对比,如果有变化,则将变化部分同步到数据库,要注意一点,自动脏数据检查只是针对persistent状态的对象,因为如果是detached状态的话,很明显sesson已经关闭,所以快照都不存在了,还对比什么啊.
得益于自动脏数据检查,Hibernate还有一个延迟写入策略(透明的交易层延迟写入) - Transparent transaction-level write-behind
例子如下:
package com.yxy.test; import java.util.Date; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.stat.Statistics; import com.yxy.bean.Student; public class HibernateWriteBehindTest { public static void main(String[] args){ SessionFactory sessionFactory = HibernateUtils.getSessionFactory(); Statistics stat = sessionFactory.getStatistics(); /* * --------------------- Session 1 --------------------------- */ Session session = sessionFactory.openSession(); session.getTransaction().begin(); stat.setStatisticsEnabled(true); stat.clear(); Student student = (Student)session.load(Student.class, 1); student.setName("Hello Hibernate !"); System.out.println(stat.getEntityLoadCount()); stat.setStatisticsEnabled(false); session.getTransaction().commit(); session.close(); /* * 开启另外一个session, 这里的student是一个detached状态对象 */ /* * --------------------- Session 2 --------------------------- */ student.setName("Another Hibernate Session"); Session anotherSession = sessionFactory.openSession(); anotherSession.getTransaction().begin(); stat.setStatisticsEnabled(true); stat.clear(); /* * update student */ anotherSession.update(student); /* * 打印出flush的次数和修改对象的次数 */ System.out.println("--------------- call update ----------------"); System.out.println(stat.getEntityUpdateCount()); System.out.println(stat.getFlushCount()); Student student2 = new Student(); student2.setName("Another Student"); student2.setRegisterDate(new Date(System.currentTimeMillis())); student2.setSex("Man"); /* * save student2 */ anotherSession.save(student2); /* * 打印出新增对象个数等信息 */ System.out.println("--------------- call save ----------------"); System.out.println(stat.getEntityInsertCount()); System.out.println(stat.getEntityUpdateCount()); System.out.println(stat.getFlushCount()); anotherSession.getTransaction().commit(); System.out.println("--------------- after commit ----------------"); System.out.println(stat.getEntityInsertCount()); System.out.println(stat.getEntityUpdateCount()); System.out.println(stat.getFlushCount()); stat.setStatisticsEnabled(false); anotherSession.close(); } }
通过console打印出来的部分信息可以看到:
--------------- call update ---------------- 0 0 Hibernate: select hibernate_sequence.nextval from dual --------------- call save ---------------- 0 0 0 Hibernate: /* insert com.yxy.bean.Student */ insert into student (name, sex, register_date, id) values (?, ?, ?, ?) Hibernate: /* update com.yxy.bean.Student */ update student set name=?, sex=?, register_date=? where id=? --------------- after commit ---------------- 1 1 1
当调用update的时候,并不会马上同步到数据库中,同样的调用save方法时也不会马上生成insert并同步到数据库,而是等到事务commit,才会一次性flush. 其实这些没有马上执行的sql会保存在之前提高过的排着队的sql那个队列里面
延迟写入的好处:
(1) 可以减少数据库的负担,不会造成频繁的写数据库,当然也可以间接的提高并发性.
(2) 可以减少数据在网络上传输的开销.
但是也不是什么情况下都等到commit才会一股脑的将所有sql送给数据库去执行.比如说在中途要执行查询操作,这时Hibernate就会把之前的SQL送给数据库去执行了.因为要保证查询到的数据时最新的.
(关于这个问题,我不能试验出来啊,奇怪,看来书上说的还是有点不对,我使用的版本是Hibernate 3.2.0.ga)
在上述例子save student2之后调用:
List students = anotherSession.createSQLQuery("select * from student").list();
但是产生的统计信息没有变化. 这表示在发出查询操作后并没有把之前的sql交给数据库去处理.
关于Persistent Context的基本概念,就先到这啦.