Hibernate一级缓存 & 二级缓存(转)
一、一级缓存Session的操作与维护
1.Hibernate对象的三种状态: transient, persistent, detached
1) transient:瞬时状态
利用new关键字创建的对象,没有与Hibernate实施交互的,也无法保证与数据库中某条记录对应的对象被称为瞬时状态,也就是生命周期非常短的意思,因为没有任何组件管理的对象非常容易被Java虚拟机回收。
例:Customer cus = new Customer();//瞬时状态对象
2) persistent:持久化状态
将瞬时状态的对象保存到Hibernate缓存中,受Hibernate管理的对象被称为持久化状态Hibernate在调用flush, close ,clear方法的时候会清理缓存保证持久化对象与底层数据库的同步,所有的持久化对象,Hibernate均会为它设置一个唯一性标识符保证其在缓存中的唯一性,这个标识符可能是hashCode,带主键查询的sql语句或者其他保证唯一的字符。
save(new object),update(new object),saveorupdate(new object),persisnt(new object)
可以将一个瞬时状态转变为持久化对象
save(new object), persistent (new object):利用select sql where id 加载一个对象
如果加载成功那么直接显示异常(插入不允许带相同标识符的组件在缓存中存在)
update(new object):不执行SQL语句,直接将对象加载入内存做更新准备
如果缓存中已经拥有一个同标识符的组件,那么显示异常,因为update只能做更新处理
无法处理两个完全一致的对象(merge(new object)可以克服这个问题)
saveorupdate(new object):利用select sql where id 加载一个对象
如果加载成功那么直接更新,否则插入
注意:业务层/表示层/应用层对于持久化状态对象的更改都会引起Hibernate的同步处理(反映到数据库中)
3) detached:游离托管状态
缓存中已经失去该对象或者该对象的标识符,虽然对象仍然存在,对象也和数据表中的记录有对应但是由于失去了Hibernate的控制,因此该对象被称为游离托管状态。
注意:业务层/表示层/应用层对于托管状态对象的更改不会会引起Hibernate的同步处理
游离托管状态的对象是指:已经经过Hibernate管理后,
因为Session.delete(),Session.close(),Session.clear(),Session.evict()
方法的执行而失去标识符的,所以托管状态和瞬时状态的对的区别是是否受过Hibernate的管理,数据表中是否有对应的记录,托管对象只有通过lock(),update(),saveorupdate()被重新加入缓存变成持久化对象才能实施数据同步
2、特殊方法:persisnt(),merge()
persisnt和save方法是差不多的,唯一的区别是针对sqlserver的identity字段的处理save为了保证持久化标识符,所以会在save的过程中就直接执行insert into....select identity();以获取最新的主键信息。
persisnt执行的时机是Transaction.commit(),Session.clear(),Session.flush()的时候
merge()与update()类似,但是区别是对于瞬时状态的理解。
假设现在有一个瞬时状态的new Customer(1),同时Session利用get(),load()方法
产生一个持久化状态的对象Session.get(Customer.class,1),这个时候使用update方法会抛出异常,而merge会将瞬时状态Customer中的属性复制到持久化状态的Customer
中。
3、查询对于一级缓存的使用
1)Session.get, Session.load方法
Session.get:迫切加载
第一查询:查询一级缓存Session缓存,如果没有那么查询二级缓存SessionFactory缓存(如果没有配置,此步省略),如果找不到那么执行Select…..From…..Where id = ?,如果数据被查到那么将查到的数据封装到对象中,对象被分别保存在一级缓存Session缓存中,和二级缓存SessionFactory缓存(如果没有配置,此步省略)。
第二查询:查询一级缓存Session缓存,,如果找不到那么执行Select…..From…..Where id = ?,如果数据被查到那么将查到的数据封装到对象中,对象被保存在一级缓存Session缓存中。
Session.load:延迟加载
Session.load认为加载始终是成功的,所以它始终不会与数据库交互,因为load认为查询一定会成功,因此只有当需要访问被加载实体属性的时候,Session.load才会按照Session.get的查询轨迹实施搜寻,不同的是Session.load始终会查询一,二级缓存再执行SQL语句
如果SQL语句无法返回对象,那么Session.load直接抛出异常。
2)Query.list, Query.iterator方法
Query.list:查询过程中不会读取任何缓存尤其是一级缓存,而是直接执行SQL语句,SQL语句有Query的HQL转换而得,执行结果会被保存在一级缓存中。
我们可以通过ehcache缓存组件为Hibernate配置查询缓存(query cache[hibernate 3.x以上版本]),这Query.list会每次查询的过程中先访问二级缓存中的查询缓存,如果没有再执行SQL语句,查询返回的结果会分别保存在一,二级缓存中。
Query.iterator:与Query.list的不同在于,它会每次访问均查询一级缓存,但是
Query.iterator记载数据的方式不是完整的SQL语句,而是N+1条SQL语句
例如:Query.list 对于一张5条记录的表的检索方式是Select ….. From Customer
而Query.iterator的检索方式是:
执行5句Select ….. From Customer where id = ?(单个对象实施记载)
二、二级缓存SessionFactory 的配置与测试
1. 利用ehcache配置Second level cache 和 Query cache
在src目录下建立ehcache.xml文件内容如下:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <ehcache> 3 <!— 4 设置对象钝化目录,二级缓存中长时间不使用或者超时的对象 5 会被保存在当前目录\java\io\tmpdir目录中,这样可以节省空间 6 --> 7 <diskStore path="java.io.tmpdir"/> 8 9 </ehcache>
1 <!-- 默认二级缓存工作模式 2 maxElementsInMemory:缓存中最大对象数 3 eternal:缓存中的对象是否永久保留 4 timeToIdleSeconds:多少毫秒后可以考虑一个对象的放入钝化目录中 5 timeToLiveSeconds:多少毫秒后可以考虑一个对象从激活状态-闲置状态 6 overflowToDisk:是否允许将闲置对象钝化入硬盘 7 diskPersistent:钝化后该对象是否允许永久无法反钝化 8 diskExpiryThreadIntervalSeconds:钝化线程间隔处理时间(毫秒) 9 memoryStoreEvictionPolicy:钝化对象选择模型(LRU:使用最少的先钝化技术) 10 --> 11 <defaultCache 12 maxElementsInMemory="10000" 13 eternal="false" 14 timeToIdleSeconds="120" 15 16 timeToLiveSeconds="120" 17 overflowToDisk="true" 18 diskPersistent="false" 19 diskExpiryThreadIntervalSeconds="120" 20 memoryStoreEvictionPolicy="LRU" 21 22 />
1 <!-- 默认二级查询缓存工作模式--> 2 <cache name="org.hibernate.cache.StandardQueryCache" 3 maxElementsInMemory="10000" 4 eternal="false" 5 timeToIdleSeconds="1800" 6 timeToLiveSeconds="0" 7 overflowToDisk="true"/>
1 <!-- 为时间邮差准备的二级查询缓存工作模式 2 主要有同步数据方法调用,例如lock(LockMode.READ) 3 --> 4 <cache name="org.hibernate.cache.UpdateTimestampsCache" 5 maxElementsInMemory="5000" 6 eternal="true" 7 timeToIdleSeconds="1800" 8 9 timeToLiveSeconds="0" 10 overflowToDisk="true"/>
2. 配置h 注意:缓存监控方法如果要能够执行,需要在hibernate.cfg.xml中设置以下配置
ibernate.cfg.xml
1 <!-- 使用ehcache第三方缓存技术 --> 2 <property name="cache.provider_class"> 3 net.sf.ehcache.hibernate.EhCacheProvider 4 </property> 5 <!-- 启用运行查询缓存 --> 6 <property name="cache.use_query_cache">true</property> 7 8 <!-- 启用二级缓存(SessionFactory缓存) --> 9 <property 10 name="cache.use_second_level_cache">true</property>
3. 为需要二级缓存管理的对象设置标识列(*.hbm.xml)
1 <hibernate-mapping> 2 <class name="cn.newtouch.myhibernate.po.Customer" schema="HIBERNATE" table="CUSTOMER"> 3 4 <!— 表示Customer需要受到缓存管理 --> 5 <cache usage="read-write"/> 6 <id name="id" type="java.lang.Long"> 7 <column name="ID" precision="8" scale="0"/> 8 9 <generator class="assigned"/> 10 </id>
4. 测试实体查询对于二级缓存的执行模式:
1 public void testSecondLevelCacheForEntityQuery() { 2 Configuration cfg = new Configuration().configure(); 3 SessionFactory factory = cfg.buildSessionFactory(); 4 Session cnn = factory.openSession(); 5 SessionStatistics ss = cnn.getStatistics(); 6 Statistics s = factory.getStatistics(); 7 cnn.get(Customer.class, 1l); 8 System.out.println("FIRST GET:" +ss.getEntityCount()); 9 System.out.println("FIRST GET PUT:" + 10 s.getSecondLevelCachePutCount()); 11 System.out.println("FIRST GET MISS:" + 12 s.getSecondLevelCacheMissCount()); 13 System.out.println("FIRST GET HIT:" + 14 s.getSecondLevelCacheHitCount()); 15 cnn.get(Customer.class, 1l); 16 17 System.out.println("SECOND GET:" +ss.getEntityCount()); 18 System.out.println("SECOND GET PUT:" + 19 s.getSecondLevelCachePutCount()); 20 System.out.println("SECOND GET MISS:" + 21 s.getSecondLevelCacheMissCount()); 22 System.out.println("HIT:" + 23 s.getSecondLevelCacheHitCount()); 24 cnn.clear(); 25 System.out.println("BEFORE CLEAR:" + ss.getEntityCount()); 26 System.out.println("BEFORE CLEAR PUT:" + 27 s.getSecondLevelCachePutCount()); 28 System.out.println("BEFORE CLEAR MISS:" + 29 s.getSecondLevelCacheMissCount()); 30 System.out.println("BEFORE CLEAR HIT:" + 31 s.getSecondLevelCacheHitCount()); 32 33 Session anotherSession = factory.openSession(); 34 anotherSession.get(Customer.class, 1l); 35 System.out.println("ANOTHER SESSION:" + 36 ss.getEntityCount()); 37 System.out.println("ANOTHER SESSION PUT:" + 38 s.getSecondLevelCachePutCount()); 39 System.out.println("ANOTHER SESSION MISS:" + 40 s.getSecondLevelCacheMissCount()); 41 System.out.println("ANOTHER SESSION HIT:" + 42 s.getSecondLevelCacheHitCount()); 43 anotherSession.clear(); 44 }
测试二级缓存的结果是
FIRST GET:1 FIRST GET PUT:1 FIRST GET MISS:1 FIRST GET HIT:0 ======================================= SECOND GET:1 SECOND GET PUT:1 SECOND GET MISS:1 SECOND GET HIT:0 ======================================= BEFORE CLEAR:0 BEFORE CLEAR PUT:1 BEFORE CLEAR MISS:1 BEFORE CLEAR HIT:0 ======================================= ANOTHER SESSION:0 ANOTHER SESSION PUT:1 ANOTHER SESSION MISS:1 ANOTHER SESSION HIT:1
5. 测试HQL查询对于二级缓存的执行模式
1 public void testSecondLevelCacheForQueries() { 2 3 Configuration cfg = new Configuration().configure(); 4 SessionFactory factory = cfg.buildSessionFactory(); 5 Session cnn = factory.openSession(); 6 7 SessionStatistics ss = cnn.getStatistics(); 8 Statistics s = factory.getStatistics(); 9 10 Query query = 11 cnn.createQuery("From Customer A"); 12 query.setCacheable(true); 13 query.setCacheRegion( 14 "cn.newtouch.myhibernate.po.Customer"); 15 query.list(); 16 System.out.println("FIRST Query:" +ss.getEntityCount()); 17 System.out.println("Level's PUT:" + 18 s.getSecondLevelCachePutCount()); 19 System.out.println("Level's MISS:" + 20 s.getSecondLevelCacheMissCount()); 21 System.out.println("Level's HIT:" + 22 s.getSecondLevelCacheHitCount()); 23 System.out.println("Queries's PUT:" + 24 s.getQueryCachePutCount()); 25 System.out.println("Queries's MISS:" + 26 s.getQueryCacheMissCount()); 27 System.out.println("Queries's HIT:" + 28 s.getQueryCacheHitCount()); 29 System.out.println("======================================="); 30 31 query = cnn.createQuery("From Customer A"); 32 query.setCacheable(true); 33 query.setCacheRegion( 34 "cn.newtouch.myhibernate.po.Customer"); 35 query.list(); 36 37 System.out.println("SECOND Query:" +ss.getEntityCount()); 38 System.out.println("Level's PUT:" + 39 s.getSecondLevelCachePutCount()); 40 System.out.println("Level's MISS:" + 41 s.getSecondLevelCacheMissCount()); 42 System.out.println("Level's HIT:" + 43 s.getSecondLevelCacheHitCount()); 44 System.out.println("Queries's PUT:" + 45 s.getQueryCachePutCount()); 46 System.out.println("Queries's MISS:" + 47 s.getQueryCacheMissCount()); 48 System.out.println("Queries's HIT:" + 49 s.getQueryCacheHitCount()); 50 System.out.println("======================================="); 51 52 cnn.clear(); 53 System.out.println("BEFORE CLEAR:" +ss.getEntityCount()); 54 System.out.println("Level's PUT:" + 55 s.getSecondLevelCachePutCount()); 56 System.out.println("Level's MISS:" + 57 s.getSecondLevelCacheMissCount()); 58 System.out.println("Level's HIT:" + 59 s.getSecondLevelCacheHitCount()); 60 System.out.println("Queries's PUT:" + 61 s.getQueryCachePutCount()); 62 System.out.println("Queries's MISS:" + 63 s.getQueryCacheMissCount()); 64 System.out.println("Queries's HIT:" + 65 s.getQueryCacheHitCount()); 66 System.out.println("======================================="); 67 68 Session anotherSession = factory.openSession(); 69 70 query = 71 anotherSession.createQuery("From Customer A"); 72 query.setCacheable(true); 73 query.setCacheRegion( 74 "cn.newtouch.myhibernate.po.Customer"); 75 query.list(); 76 77 System.out.println("ANOTHER SESSION:" +ss.getEntityCount()); 78 System.out.println("Level's PUT:" + 79 s.getSecondLevelCachePutCount()); 80 System.out.println("Level's MISS:" + 81 s.getSecondLevelCacheMissCount()); 82 System.out.println("Level's HIT:" + 83 s.getSecondLevelCacheHitCount()); 84 System.out.println("Queries's PUT:" + 85 s.getQueryCachePutCount()); 86 System.out.println("Queries's MISS:" + 87 s.getQueryCacheMissCount()); 88 System.out.println("Queries's HIT:" + 89 s.getQueryCacheHitCount()); 90 System.out.println("======================================="); 91 92 anotherSession.clear(); 93 }
测试二级缓存的结果是:
1 FIRST Query:2 2 Level's PUT:2 3 Level's MISS:0 4 Level's HIT:0 5 Queries's PUT:1 6 Queries's MISS:1 7 Queries's HIT:0 8 ======================================= 9 SECOND Query:2 10 Level's PUT:2 11 Level's MISS:0 12 Level's HIT:0 13 Queries's PUT:1 14 Queries's MISS:1 15 Queries's HIT:1 16 ======================================= 17 BEFORE CLEAR:0 18 Level's PUT:2 19 Level's MISS:0 20 Level's HIT:0 21 Queries's PUT:1 22 Queries's MISS:1 23 Queries's HIT:1 24 ======================================= 25 ANOTHER SESSION:0 26 Level's PUT:2 27 Level's MISS:0 28 Level's HIT:2 29 Queries's PUT:1 30 Queries's MISS:1 31 Queries's HIT:2
注意:缓存监控方法如果要能够执行,需要在hibernate.cfg.xml中设置以下配置
1 <property name="generate_statistics">true</property>
以下情况适合使用二级缓存:
1、很少被修改的数据
2、不是很重要的数据,允许出现偶尔并发的数据
3、不会被并发访问的数据
4、参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。