为何要使用缓存?
把常用数据库持久化对象,作为缓存保存在内存中,减少与数据库交互次数,可以提高性能
一、什么是一级缓存?
就是Session级别的缓存, 属于事务范围。hibernate框架内置。一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,
如果短时间内这个 session(同一个session)又做了同一个操作,那么hibernate直接从一级缓存中取数据,而不会再去连数据库,取数据;
二、什么是二级缓存?
就是SessionFactory级别的缓存,属于进程范围的缓存。需引入外部缓存插件并配置。(不是同一个session)是查询的时候会把查询结果缓存到二级 缓存中,如果同一个sessionFactory 创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库;
三、Hibernate中提供了两级别缓存
第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;
第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载 和卸载。
Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存;
四、一级缓存的使用
数据 放入缓存:
1. save()。当session对象调用save()方法保存一个对象后,该对象会被放入到session的缓存中。
2. get()和load()。当session对象调用get()或load()方法从数据库取出一个对象后,该对象也会被放入到session的缓存中。
3. 使用HQL和QBC等从数据库中查询数据。
@Test public void list1() { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); try { Mydept dept = (Mydept) session.get(Mydept.class, 60L); System.out.println(dept.getDname() + "\t" + dept.getLoc()); // session.clear(); System.out.println("---------------->再次查询!!"); Mydept dept1 = (Mydept) session.get(Mydept.class, 60L); System.out.println(dept1.getDname() + "\t" + dept1.getLoc()); /* 比较两个get()方法获取的对象是否是同一个对象 */ System.out.println(dept == dept1); tx.commit(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(); } }
显示结果:
其原理是:在同一个Session里面,第一次调用get()方法, Hibernate先检索缓存中是否有该查找对象,发现没有,Hibernate发送SELECT语句到数据库中取出相应的对象,然后将该对象放入缓存中,以便下次使用,第二次调用get()方法,Hibernate先检索缓存中是否有该查找对象,发现正好有该查找对象,就从缓存中取出来,不再去数据库中检索,没有再次发送select语句。
数据从缓存中清除:
1. evit()将指定的持久化对象从缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象。
2. clear()将缓存中的所有持久化对象清除,释放其占用的内存资源。
其他缓存操作:
1. contains()判断指定的对象是否存在于缓存中。
2. flush()刷新缓存区的内容,使之与数据库数据保持同步。
五、二级缓存Hibernate的二级缓存同一级缓存一样,也是针对对象ID来进行缓存。所以说,二级缓存的作用范围是针对根据ID获得对象的查询。
(1) 二级缓存的工作可以概括为以下几个部分:
● 在执行各种条件查询时,如果所获得的结果集为实体对象的集合,那么就会把所有的数据对象根据ID放入到二级缓存中。
● 当Hibernate根据ID访问数据对象的时候,首先会从Session一级缓存中查找,如果查不到并且配置了二级缓存,那么会从二级缓存中查找,如果还查不到,就会查询数据库,把结果按照ID放入到缓存中。
● 删除、更新、增加数据的时候,同时更新缓存。
(2)二级缓存应用的场合:
在这里特别要注意的是对放入缓存中的数据不能有第三方的应用对数据进行更改(其中也包括在自己程序中使用其他方式进行数据的修改,
例如,JDBC),因为那样Hibernate将不会知道数据已经被修改,也就无法保证缓存中的数据与数据库中数据的一致性。
(3)二级缓存组件
在默认情况下,Hibernate会使用EHCache作为二级缓存组件。但是,可以通过设置 cache.region.factory_class属性,指定其他的缓存策略,该缓存策略必须实现org.hibernate.cache.ehcache.EhCacheRegionFactory接口。
Hibernate内置支持的二级缓存组件如表所示。
(4) 二级缓存的配置
1、当然首先引入EHCache相关的jar包
在Hibernate官方网站下载hibernate-release-4.2.2.Final的压缩包并解压,引入hibernate-release-4.2.2.Final\lib\optional\ehcache目录下的ehcache-core-2.4.3.jar、hibernate-ehcache-4.2.2.Final.jar、slf4j-api-1.6.1.jar三个jar包即可。
2、在项目classpath的根目录下面写好EHCache的配置文件ehcache.xml
此配置文件可以直接到hibernate-release-4.2.2.Final\project\hibernate-ehcache\src\test\resources\ehcache.xml拷贝到src目录下即可。
4、配置哪些实体类的对象需要二级缓存
有两种方式:
1)、在实体类的映射文件里面配置
<cache usage="read-only"/>
注意:<cache>放在<id>之前
2)、在Hibernate配置文件hibernate.cfg.xml中统一配置(推荐)
<class-cache usage="read-only" class="com.netsix.domain.Student"/>
注意:class这里指定你需要使用二级缓存的类
5、Hibenrate的二级缓存EHCache就启用了,更多详细配置和二级缓存的管理请参考官方提供的手册及Hibenrate提供
@Test public void list2() { try { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); Mydept dept = (Mydept) session.get(Mydept.class, 60L); System.out.println(dept.getDname() + "\t" + dept.getLoc()); tx.commit(); HibernateUtil.closeSession(); System.out.println("---------------->再次查询!!"); Session session1 = HibernateUtil.currentSession(); Transaction tx1 = session1.beginTransaction(); Mydept dept1 = (Mydept) session1.get(Mydept.class, 60L); System.out.println(dept1.getDname() + "\t" + dept1.getLoc()); tx1.commit(); HibernateUtil.closeSession(); /* 比较两个get()方法获取的对象是否是同一个对象 */ System.out.println(dept == dept1); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); // tx.rollback(); } finally { // HibernateUtil.closeSession(); } }
注意:list方法只能写,不能读二级缓存。
Query的iterate方法返回List集合的迭代器对象Iterator。使用Query的iterate方法遍历时,集合中保存的是只有OID的代理对象,当访问对象的其它属性时,才进行初始化。初始化时,优先查找二级缓存;如果缓存中不存在,则生成SQL语句查询数据库。
当使用二级缓存时,iterate比list 效率要好。
@Test public void list3() { try { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); Iterator<Mydept> list = session.createQuery( "from Mydept where deptno<30").iterate(); // 3 while (list.hasNext()) { Mydept dept = list.next(); System.out.println(dept.getDname() + "\t" + dept.getLoc()); } tx.commit(); HibernateUtil.closeSession(); System.out.println("---------------->再次查询!!"); session = HibernateUtil.currentSession(); Transaction tx1 = session.beginTransaction(); list = session.createQuery("from Mydept where deptno<40").iterate(); // 5 while (list.hasNext()) { Mydept dept = list.next(); System.out.println(dept.getDname() + "\t" + dept.getLoc()); } tx1.commit(); HibernateUtil.closeSession(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); // tx.rollback(); } finally { } }显示结果:
六、 查询缓存:这里的查询缓存key是HQL语句或SQL语句,value是查询结果数据。查询缓存依赖二级缓存。
1) 在hibernate.cfg.xml中添加:
hibernate-configuration> <session-factory> <!-- 启用查询缓存 --> <property name= "hibernate.cache.use_query_cache">true</property>
@Test public void test4() { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); //手动设置启用查询缓存 List<Dept> dept = session.createQuery("from Dept where deptno<30").setCacheable(true) .list(); for (Dept dept1 : dept) { System.out.println(dept1.getDeptno()); } tx.commit(); HibernateUtil.closeSession(); System.out.println("-----------------》再次查询数据------------->"); Session session1 = HibernateUtil.currentSession(); Transaction tx1 = session1.beginTransaction(); //手动设置启用查询缓存 dept = session1.createQuery("from Dept where deptno<30").setCacheable(true).list(); for (Dept dept1 : dept) { System.out.println(dept1.getDeptno()); } tx1.commit(); HibernateUtil.closeSession(); }显示结果: