Hibernate part 17:二级缓存


 

Session级别的缓存,事务范围的

SessionFactory级别的缓存,进程范围的

 

SessionFactory缓存:

内置:Hibernate自带的,只能缓存一些配置的SQL语句,如命名查询配置在*.hbm.xml中的SQL语句

外置:需要配置第三方插件使用,自己内有实现

 

二级缓存的结构



 

二级缓存提供商:

EHCache: 可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持
OpenSymphony `:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存

 

二级缓存并发访问策略:

非严格读写(Nonstrict-read-write): 不保证缓存与数据库中数据的一致性. 提供 Read Uncommited 事务隔离级别, 对于极少被修改, 而且允许脏读的数据, 可以采用这种策略
读写型(Read-write): 提供 Read Commited 数据隔离级别.对于经常读但是很少被修改的数据, 可以采用这种隔离类型, 因为它可以防止脏读
事务型(Transactional): 仅在受管理环境下适用. 它提供了 Repeatable Read 事务隔离级别. 对于经常读但是很少被修改的数据, 可以采用这种隔离类型, 因为它可以防止脏读和不可重复读
只读型(Read-Only):提供 Serializable 数据隔离级别, 对于从来不会被修改的数据, 可以采用这种访问策略(很强,但是性能低

 

二级缓存的配置:

 

1、ehcache依赖jar包:ehcache-1.5.0.jar、commons-logging.jar、backport-util-concurrent.jar

 

2、在hibernate.cfg.cml配置文件中配置使用二级缓存

<property name="hibernate.cache.use_second_level_cache">true</property>

3、 配置二级缓存提供商


<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

 4、配置二级缓存的并发策略


位置一:*.hbm.xml中


<hibernate-mapping>
<class name="rock.lee.bean.Customer" table="customer" catalog="test" >
<cache usage="read-write"/>

 位置二:hibernate.cfg.xml

<class-cache usage="read-write" class="rock.lee.bean.Customer"/>
<class-cache usage="read-write" class="rock.lee.bean.Order"/>
<collection-cache usage="read-write" collection="rock.lee.bean.Customer.orders"/>

 5、配置ehcache.xml,解压ehcache-1.5.0.jar中获取

配置完成

 

案例一:证明二级缓存是存在的

@Test
public void test01() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//SQL
Customer c1 = (Customer) session.get(Customer.class, 1);
System.out.println(c1);
//从一级缓存中获取
Customer c2 = (Customer) session.get(Customer.class, 1);
System.out.println(c2);
transaction.commit();//session close

session = HibernateUtils.getCurrentSession();
transaction = session.beginTransaction();
//从二级缓存中获取
Customer c3 = (Customer) session.get(Customer.class, 1);
System.out.println(c3);

transaction.commit();
}

 修改hibernate.cfg.xml关闭二级缓存, 再次执行查询

<property name="hibernate.cache.use_second_level_cache">false</property>

 


案例二:类级别缓冲区的散装数据结构


在Customer类中的toString()中调用Object的toString()打印地址


@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", city=" + city+"]"
+ super.toString();
}

 还是运行案例一的程序,输出结果

Customer [id=1, name=林允儿, city=SH]rock.lee.bean.Customer@3003e926
Customer [id=1, name=林允儿, city=SH]rock.lee.bean.Customer@3003e926//来自一级缓存的对象
Customer [id=1, name=林允儿, city=SH]rock.lee.bean.Customer@4cf221f6//来自二级缓存的对象

 

案例三:get/load可以对二级缓存读写,Query的list对二级缓存只能写不能读

@Test
public void test02() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//查询SQL,将数据写入二级缓存
List<Customer> list = session.createQuery("from Customer").list();
System.out.println(list.size());
transaction.commit();// session close
session = HibernateUtils.getCurrentSession();
transaction = session.beginTransaction();
//不能读取二级缓存数据,执行SQL查询,将数据写入二级缓存
list = session.createQuery("from Customer").list();
System.out.println(list.size());

transaction.commit();// session close
session = HibernateUtils.getCurrentSession();
transaction = session.beginTransaction();
//从二级缓存中获取
Customer c1 = (Customer) session.get(Customer.class, 1);
System.out.println(c1);

transaction.commit();
}

 

案例四:集合级别缓冲区的存在

@Test
public void test03() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

Customer c1 = (Customer) session.get(Customer.class, 1);
System.out.println(c1.getOrders());

transaction.commit();
session = HibernateUtils.getCurrentSession();
session.beginTransaction();

Customer c2 = (Customer) session.get(Customer.class, 1);
//通过二级换获得Order数据
System.out.println(c2.getOrders());

transaction.commit();
}

 

案例五:集合级别缓冲区依赖类级别缓冲区

在hibernate.cfg.xml中注释掉Order类级别缓存

<!-- <class-cache usage="read-write" class="rock.lee.bean.Order" /> -->

 执行案例四同样的代码,如果Order类级别缓冲区关系,集合缓冲区也无法使用


@Test
public void test03() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

Customer c1 = (Customer) session.get(Customer.class, 1);
System.out.println(c1.getOrders());

transaction.commit();
session = HibernateUtils.getCurrentSession();
session.beginTransaction();

Customer c2 = (Customer) session.get(Customer.class, 1);
//通SQL获得数据
System.out.println(c2.getOrders());

transaction.commit();
}

 集合级别的缓冲区只会缓存id,然后去类级别缓冲区里查询数据

 

案例六:一级缓存数据自动同步到二级缓存

@Test
public void test04() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

Customer c1 = (Customer) session.get(Customer.class, 1);
System.out.println(c1);
//快照比对后自动更新,并且同步到二级缓存
c1.setCity("GD");

transaction.commit();
session = HibernateUtils.getCurrentSession();
session.beginTransaction();

//从二级缓存中获取修改后的数据
Customer c2 = (Customer) session.get(Customer.class, 1);
System.out.println(c2);
}

 

 案例七:将二级换数据保存到硬盘

保存到硬盘中的位置在ehcache.xml中配置

#默认目录C:\Users\ADMINI~1\AppData\Local\Temp\
<diskStore path="java.io.tmpdir"/>

  将D:\ehcache目录作为缓存目录,maxElementsInMemory配置为3

  <diskStore path="D:\ehcache"/>
<defaultCache
maxElementsInMemory="3" 内存中最大元素数量(当内存对数量超过这个数,才会缓存到硬盘)
eternal="false" 缓存数据是否永久有效
timeToIdleSeconds="120" 设置对象空闲最长时间 ,超过时间缓存对象如果没用,回收
timeToLiveSeconds="120" 设置对象存活最长时间, 无论对象是否使用,到时间回收
overflowToDisk="true" 当内存缓存数据达到上限,是否缓存到硬盘
maxElementsOnDisk="10000000" 硬盘上最大缓存对象数量
diskPersistent="false" 当jvm结束时是否持久化对象 true false 默认是false
diskExpiryThreadIntervalSeconds="120" 专门用于清除过期对象的监听线程的轮询时
memoryStoreEvictionPolicy="LRU"
/>
 由于JUnit执行后会System.exit(0),所以磁盘文件大小为0,通过断点,让程序有时间将数据保存到磁盘,加休眠也可以
@Test
@SuppressWarnings("unchecked")
public void test05() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

System.out.println(System.getProperty("java.io.tmpdir"));

List<Order> list = session.createQuery("from Order").list();
System.out.println(list.size());

transaction.commit();
}

 

 案例八:更新时间戳缓冲区

时间戳缓存区域存放了对于查询结果相关的表进行插入, 更新或删除操作的时间戳.  Hibernate 通过时间戳缓存区域来判断被缓存的查询结果是否过期

 

@Test
public void test06() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

Customer c1 = (Customer) session.get(Customer.class, 2);
// 不能这么修改,一级缓存会自动同步到二缓存
// c1.setCity("BJ");

session.createSQLQuery("update customer set city='BJ' where id=1").executeUpdate();

transaction.commit();
session = HibernateUtils.getCurrentSession();
transaction = session.beginTransaction();

//判断类别缓冲区中的时间戳和时间戳缓冲区的是否一样,不一样,重新发送SQL查询
c1 = (Customer) session.get(Customer.class, 1);
System.out.println(c1);

transaction.commit();

}

 

 案例九:Query接口的iterator(),返回迭代器代理对象,数据中只有id,在访问每个元素时,先查找二级缓存,如果找不到,生成SQL语句

@Test
public void test07() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

List<Order> list = (List<Order>) session.createQuery("from Order where id < 5").list();

transaction.commit();
session = HibernateUtils.getCurrentSession();
transaction = session.beginTransaction();

//不在缓存里的,会执行SQL查询
Iterator<Order> iterate = session.createQuery("from Order where id <10").iterate();
while (iterate.hasNext()) {
Order o = (Order) iterate.next();
System.out.println(o.getId()+"-->"+o.getAddress());
}

}

 

案例十:查询缓存

二级缓存查询结果,比如以OID作为key ,以对象作为Value 进行缓存 , 查询缓存 以SQL语句为key ,以查询结果作为Value

在hibernate.cfg.xml中配置开启查询缓存

<property name="hibernate.cache.use_query_cache">true</property>

 代码中也要启用查询缓存


@Test
public void test08() {
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

Query query = session.createQuery("select name from Customer");
query.setCacheable(true);// 启用查询缓存
List<String> list = query.list();

transaction.commit();
session = HibernateUtils.getCurrentSession();
transaction = session.beginTransaction();

//从查询缓存中获取数据,不会有SQL产生
query = session.createQuery("select name from Customer");
query.setCacheable(true);// 启用查询缓存
List<String> lst = query.list();
System.out.println(lst);

}

 

 案例十一:二级缓存性能检测

在hibernate.cfg.xml配置开启性能检测

<property name="hibernate.generate_statistics">true</property>

 二级缓存命中率


// 二级缓存的性能监测
public void test09() {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
// sessionFactory.getStatistics().setStatisticsEnabled(true);
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();

// 放入二级缓存
Customer customer1 = (Customer) session.get(Customer.class, 1); // 不在一级缓存,不在二级缓存 miss+1
Customer customer2 = (Customer) session.get(Customer.class, 2); // 不在一级缓存,不在二级缓存 miss+1
Customer customer3 = (Customer) session.get(Customer.class, 2); // 在一级缓存找到,hit和miss都不+1

transaction.commit();
session = sessionFactory.getCurrentSession();
transaction = session.beginTransaction();

// 读二级缓存
Customer customer4 = (Customer) session.get(Customer.class, 1); // 在二级缓存找到hit+1
Customer customer5 = (Customer) session.get(Customer.class, 2); // 在二级缓存找到 hit+1


transaction.commit();
// 监测
System.out.println("命中次数: " + sessionFactory.getStatistics().getSecondLevelCacheHitCount()); // 命中次数
System.out.println("MISS次数:" + sessionFactory.getStatistics().getSecondLevelCacheMissCount()); // 丢失次数

}

 

 

 

 

 

 

 

 

 

 

 


本站声明
本文转载自:http://mvplee.iteye.com/blog/2239557     作者:mvplee     发布日期:2015-08-31     本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。


 
© 2014-2016 ITdaan.com 粤ICP备14056181号