Hibernate一级缓存 & 二级缓存

Hibernate First Level Cache & Second Level Cache

                                                                                           原创文章,欢迎加入我们群:网络技术交流(118521779)

一、一级缓存Session的操作与维护

 

1.Hibernate对象的三种状态transientpersistentdetached

 

1) transient:瞬时状态           

利用new关键字创建的对象,没有与Hibernate实施交互的,也无法保证与数据库中某条记录对应的对象被称为瞬时状态,也就是生命周期非常短的意思,因为没有任何组件管理的对象非常容易被Java虚拟机回收。

:Customer cus = new Customer();//瞬时状态对象

 

2) persistent:持久化状态

将瞬时状态的对象保存到Hibernate缓存中,受Hibernate管理的对象被称为持久化状态Hibernate在调用flushclose ,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()

persisntsave方法是差不多的,唯一的区别是针对sqlserveridentity字段的处理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、查询对于一级缓存的使用

1Session.getSession.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直接抛出异常。

 

2Query.listQuery.iterator方法

Query.list查询过程中不会读取任何缓存尤其是一级缓存,而是直接执行SQL语句,SQL语句有QueryHQL转换而得,执行结果会被保存在一级缓存中。

我们可以通过ehcache缓存组件为Hibernate配置查询缓存(query cache[hibernate 3.x以上版本]),这Query.list会每次查询的过程中先访问二级缓存中的查询缓存,如果没有再执行SQL语句,查询返回的结果会分别保存在一,二级缓存中。

 

Query.iterator:与Query.list的不同在于,它会每次访问均查询一级缓存,但是

Query.iterator记载数据的方式不是完整的SQL语句,而是N+1SQL语句

例如:Query.list 对于一张5条记录的表的检索方式是Select ….. From Customer

Query.iterator的检索方式是:

执行5Select ….. From Customer where id = ?(单个对象实施记载)

  

二、二级缓存SessionFactory 的配置与测试

1. 利用ehcache配置Second level cache 和 Query cache

src目录下建立ehcache.xml文件内容如下:

 
 
 
     
<? xml version="1.0" encoding="UTF-8" ?>
< ehcache >
<!
设置对象钝化目录,二级缓存中长时间不使用或者超时的对象
会被保存在当前目录\java\io\tmpdir目录中,这样可以节省空间
--
>
< diskStore path ="java.io.tmpdir" />
</ ehcache >

 
 
     
<!-- 默认二级缓存工作模式
maxElementsInMemory:缓存中最大对象数
eternal:缓存中的对象是否永久保留
timeToIdleSeconds:多少毫秒后可以考虑一个对象的放入钝化目录中
timeToLiveSeconds:多少毫秒后可以考虑一个对象从激活状态-闲置状态
overflowToDisk:是否允许将闲置对象钝化入硬盘
diskPersistent:钝化后该对象是否允许永久无法反钝化
diskExpiryThreadIntervalSeconds:钝化线程间隔处理时间(毫秒)
memoryStoreEvictionPolicy:钝化对象选择模型(LRU:使用最少的先钝化技术)
-->
< defaultCache
maxElementsInMemory ="10000"
eternal
="false"
timeToIdleSeconds
="120"
timeToLiveSeconds
="120"
overflowToDisk
="true"
diskPersistent
="false"
diskExpiryThreadIntervalSeconds
="120"
memoryStoreEvictionPolicy
="LRU"
/>

  

 
     
<!-- 默认二级查询缓存工作模式 -->
< cache name ="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory
="10000"
eternal
="false"
timeToIdleSeconds
="1800"
timeToLiveSeconds
="0"
overflowToDisk
="true" />

 
 
     
<!-- 为时间邮差准备的二级查询缓存工作模式
主要有同步数据方法调用,例如lock(LockMode.READ)
-->
< cache name ="org.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory
="5000"
eternal
="true"
timeToIdleSeconds
="1800"
timeToLiveSeconds
="0"
overflowToDisk
="true" />

  

2. 配置hibernate.cfg..xml

 
     
<!-- 使用ehcache第三方缓存技术 -->
< property name ="cache.provider_class" >
net.sf.ehcache.hibernate.EhCacheProvider
</ property >
<!-- 启用运行查询缓存 -->
< property name ="cache.use_query_cache" > true </ property >
<!-- 启用二级缓存(SessionFactory缓存) -->
< property
name ="cache.use_second_level_cache" > true </ property >
 

3. 为需要二级缓存管理的对象设置标识列(*.hbm.xml)

 
     
< hibernate-mapping >
< class name ="cn.newtouch.myhibernate.po.Customer" schema ="HIBERNATE" table ="CUSTOMER" >
<! — 表示Customer需要受到缓存管理 -- >
< cache usage ="read-write" />
< id name ="id" type ="java.lang.Long" >
< column name ="ID" precision ="8" scale ="0" />
< generator class ="assigned" />
</ id >

4. 

测试实体查询对于二级缓存的执行模式:
 
     
public void testSecondLevelCacheForEntityQuery() {
Configuration cfg
= new Configuration().configure();
SessionFactory factory
= cfg.buildSessionFactory();
Session cnn
= factory.openSession();
SessionStatistics ss
= cnn.getStatistics();
Statistics s
= factory.getStatistics();
cnn.get(Customer.
class , 1l );
System.out.println(
" FIRST GET: " + ss.getEntityCount());
System.out.println(
" FIRST GET PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" FIRST GET MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" FIRST GET HIT: " +
s.getSecondLevelCacheHitCount());
cnn.get(Customer.
class , 1l );

System.out.println(
" SECOND GET: " + ss.getEntityCount());
System.out.println(
" SECOND GET PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" SECOND GET MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" HIT: " +
s.getSecondLevelCacheHitCount());
cnn.clear();
System.out.println(
" BEFORE CLEAR: " + ss.getEntityCount());
System.out.println(
" BEFORE CLEAR PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" BEFORE CLEAR MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" BEFORE CLEAR HIT: " +
s.getSecondLevelCacheHitCount());

Session anotherSession
= factory.openSession();
anotherSession.get(Customer.
class , 1l );
System.out.println(
" ANOTHER SESSION: " +
ss.getEntityCount());
System.out.println(
" ANOTHER SESSION PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" ANOTHER SESSION MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" ANOTHER SESSION HIT: " +
s.getSecondLevelCacheHitCount());
anotherSession.clear();
}

  

测试二级缓存的结果是:
 
     
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查询对于二级缓存的执行模式:
 
     
public void testSecondLevelCacheForQueries() {

Configuration cfg
= new Configuration().configure();
SessionFactory factory
= cfg.buildSessionFactory();
Session cnn
= factory.openSession();

SessionStatistics ss
= cnn.getStatistics();
Statistics s
= factory.getStatistics();

Query query
=
cnn.createQuery(
" From Customer A " );
query.setCacheable(
true );
query.setCacheRegion(
" cn.newtouch.myhibernate.po.Customer " );
query.list();
System.out.println(
" FIRST Query: " + ss.getEntityCount());
System.out.println(
" Level's PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" Level's MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" Level's HIT: " +
s.getSecondLevelCacheHitCount());
System.out.println(
" Queries's PUT: " +
s.getQueryCachePutCount());
System.out.println(
" Queries's MISS: " +
s.getQueryCacheMissCount());
System.out.println(
" Queries's HIT: " +
s.getQueryCacheHitCount());
System.out.println(
" ======================================= " );

query
= cnn.createQuery( " From Customer A " );
query.setCacheable(
true );
query.setCacheRegion(
" cn.newtouch.myhibernate.po.Customer " );
query.list();

System.out.println(
" SECOND Query: " + ss.getEntityCount());
System.out.println(
" Level's PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" Level's MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" Level's HIT: " +
s.getSecondLevelCacheHitCount());
System.out.println(
" Queries's PUT: " +
s.getQueryCachePutCount());
System.out.println(
" Queries's MISS: " +
s.getQueryCacheMissCount());
System.out.println(
" Queries's HIT: " +
s.getQueryCacheHitCount());
System.out.println(
" ======================================= " );

cnn.clear();
System.out.println(
" BEFORE CLEAR: " + ss.getEntityCount());
System.out.println(
" Level's PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" Level's MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" Level's HIT: " +
s.getSecondLevelCacheHitCount());
System.out.println(
" Queries's PUT: " +
s.getQueryCachePutCount());
System.out.println(
" Queries's MISS: " +
s.getQueryCacheMissCount());
System.out.println(
" Queries's HIT: " +
s.getQueryCacheHitCount());
System.out.println(
" ======================================= " );

Session anotherSession
= factory.openSession();

query
=
anotherSession.createQuery(
" From Customer A " );
query.setCacheable(
true );
query.setCacheRegion(
" cn.newtouch.myhibernate.po.Customer " );
query.list();

System.out.println(
" ANOTHER SESSION: " + ss.getEntityCount());
System.out.println(
" Level's PUT: " +
s.getSecondLevelCachePutCount());
System.out.println(
" Level's MISS: " +
s.getSecondLevelCacheMissCount());
System.out.println(
" Level's HIT: " +
s.getSecondLevelCacheHitCount());
System.out.println(
" Queries's PUT: " +
s.getQueryCachePutCount());
System.out.println(
" Queries's MISS: " +
s.getQueryCacheMissCount());
System.out.println(
" Queries's HIT: " +
s.getQueryCacheHitCount());
System.out.println(
" ======================================= " );

anotherSession.clear();

 

测试二级缓存的结果是:

 
     
FIRST Query: 2
Level
' s PUT:2
Level ' s MISS:0
Level ' s HIT:0
Queries ' s PUT:1
Queries ' s MISS:1
Queries ' s HIT:0
=======================================
SECOND Query:
2
Level
' s PUT:2
Level ' s MISS:0
Level ' s HIT:0
Queries ' s PUT:1
Queries ' s MISS:1
Queries ' s HIT:1
=======================================
BEFORE CLEAR:
0
Level
' s PUT:2
Level ' s MISS:0
Level ' s HIT:0
Queries ' s PUT:1
Queries ' s MISS:1
Queries ' s HIT:1
=======================================
ANOTHER SESSION:
0
Level
' s PUT:2
Level ' s MISS:0
Level ' s HIT:2
Queries ' s PUT:1
Queries ' s MISS:1
Queries ' s HIT:2

 

  

注意:缓存监控方法如果要能够执行,需要在hibernate.cfg.xml中设置以下配置
 
     
< property name ="generate_statistics" > true </ property >

以下情况适合使用二级缓存:

1很少被修改的数据

2不是很重要的数据,允许出现偶尔并发的数据 

3不会被并发访问的数据

4参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。

posted on 2011-04-17 13:51  我是HackAdmin他爹 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/penghaijun/articles/2018840.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值