Hibernate中的一级缓存和二级缓存

缓存(Cache): 计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存。

Hibernate中提供了两个级别的缓存
第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的,是hibernate内置的,一般情况下无需进行干预。
第二级别的缓存是 SessionFactory 级别的缓存,它是属于进程范围的缓存。


一级缓存
在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库

Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷出缓存(flush)

默认情况下 Session 在以下时间点刷出缓存:
当应用程序调用 Transaction 的 commit()方法的时, 该方法先刷出缓存(session.flush()),然后在向数据库提交事务(tx.commit())
当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态
调用 Session 的 flush() 方法


二级缓存
SessionFactory 的缓存可以分为两类:
内置缓存: Hibernate 自带的, 不可卸载. 通常在 Hibernate 的初始化阶段, Hibernate 会把映射元数据和预定义的 SQL 语句放到 SessionFactory 的缓存中, 映射元数据是映射文件中数据的复制, 而预定义 SQL 语句时 Hibernate 根据映射元数据推到出来的. 该内置缓存是只读的.
外置缓存(二级缓存): 一个可配置的缓存插件. 在默认情况下, SessionFactory 不会启用这个缓存插件. 外置缓存中的数据是数据库数据的复制, 外置缓存的物理介质可以是内存或硬盘

并发访问策略
transactional(事务型):
仅在受管理的环境中适用
提供Repeatable Read事务隔离级别
适用经常被读,很少修改的数据
可以防止脏读和不可重复读的并发问题
缓存支持事务,发生异常的时候,缓存也能够回滚

read-write(读写型):
提供Read Committed事务隔离级别
在非集群的环境中适用
适用经常被读,很少修改的数据
可以防止脏读
更新缓存的时候会锁定缓存中的数据

nonstrict-read-write(非严格读写型):
适用极少被修改,偶尔允许脏读的数据(两个事务同时修改数据的情况很少见)
不保证缓存和数据库中数据的一致性
为缓存数据设置很短的过期时间,从而尽量避免脏读
不锁定缓存中的数据

read-only(只读型):
适用从来不会被修改的数据(如参考数据)
在此模式下,如果对数据进行更新操作,会有异常
事务隔离级别低,并发性能高
在集群环境中也能完美运作

适合放入二级缓存中的数据:
很少被修改
不是很重要的数据, 允许出现偶尔的并发问题

不适合放入二级缓存中的数据:
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据

二级缓存提供的供应商
二级缓存是可配置的的插件, Hibernate 允许选用以下类型的缓存插件:
EHCache: 可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持
OpenSymphony `:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存


二级缓存的配置(EHCache):

jar包:
ehcache-1.5.0.jar
backport-util-concurrent-2.1.jar
commons-logging-1.1.1.jar

配置文件hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///hibernate?useUnicode=true&amp;characterEncoding=UTF-8</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">1234</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

		<property name="hibernate.show_sql">true</property>
		<property name="hibernate.format_sql">true</property>
		
		<property name="hibernate.hbm2ddl.auto">update</property>
		
		<property name="javax.persistence.validation.mode">none</property>
		
		<!--  配置c3p0连接池 -->
		<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
		
		<!-- 隔离级别 -->
		<property name="hibernate.connection.isolation">4</property>
		
		<!-- 配置session与本地线程绑定 -->
		<property name="hibernate.current_session_context_class">thread</property>
		
		<!--  开启二级缓存 -->
		<property name="hibernate.cache.use_second_level_cache">true</property>
		
		<!-- 确定二级缓存供应商 -->
		<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
		
		<!--  二级缓存 ## 查询缓存 -->
		<property name="hibernate.cache.use_query_cache">true</property>
		
		<!-- 5 添加映射配置文件 -->
		<mapping resource="com/my/bean/Customer.hbm.xml"/>
		<mapping resource="com/my/bean/Order.hbm.xml"/>
		
		<!--配置需要缓存的 类或集合 -->
		<!-- 类级别缓存 -->
		<class-cache usage="read-write" class="com.my.bean.Customer"/>
		<!-- --><class-cache usage="read-write" class="com.my.bean.Order"/> 
		<!-- 集合级别缓存 -->
		<collection-cache usage="read-write" collection="com.my.bean.Customer.orderSet"/>
	
	</session-factory>

</hibernate-configuration>

配置ehcache.xml

<diskStore>:指定一个目录, 当 EHCache 把数据写到硬盘上时, 将把数据写到这个文件目录下.  默认是C:\WINDOWS\Temp 
<defaultCache>: 设置缓存的默认数据过期策略 
<cache> 设定具体的命名缓存的数据过期策略
   使用name属性,cn.itcast.second.Order
每个命名缓存代表一个缓存区域,每个缓存区域有各自的数据过期策略。命名缓存机制使得用户能够在每个类以及类的每个集合的粒度上设置数据过期策略。 


demo:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="c:/ehcache"/>

    <defaultCache
            maxElementsInMemory="5"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
</ehcache>


测试类:

package com.my.bean;

import java.util.List;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.itcast.a_query.Customer;
import cn.itcast.a_query.Order;
import cn.itcast.util.SessionUtils;

public class TestTwoCache {
	
	@Test
	public void demo01(){
		/* 测试 :二级缓存是否存在
		 * 如果二级缓存开,只执行1次查询
		 * 如果二级缓存关,将执行2次查询
		 */
		
		Session session = SessionUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer customer = (Customer) session.get(Customer.class, 3); //执行查询,将数据存放到一级缓存,与此也放入到了二级缓存
		System.out.println(customer.getName());
		
		Customer customer33 = (Customer) session.get(Customer.class, 3); //一级缓存获得数据
		System.out.println(customer33.getName());
		
		transaction.commit();
		session.close();
		
		
		Session session2 = SessionUtils.openSession();
		Transaction transaction2 = session2.beginTransaction();
		
		Customer customer2 = (Customer) session2.get(Customer.class, 3); //session 一级缓存失效,从二级缓存获得数据
		System.out.println(customer2.getName());
		
		transaction2.commit();
		session2.close();
		
		
		
		
	}
	
	@Test
	public void demo02(){
		/* 保存类级别数据时,将以散装数据形式保存。
		 */
		
		Session session = SessionUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer customer = (Customer) session.get(Customer.class, 3);
		System.out.println(customer);
		
		transaction.commit();
		session.close();
		
		Session session2 = SessionUtils.openSession();
		Transaction transaction2 = session2.beginTransaction();
		
		Customer customer2 = (Customer) session2.get(Customer.class, 3); //session 一级缓存失效,从二级缓存获得数据
		System.out.println(customer2);
		
		transaction2.commit();
		session2.close();
		
		
		
		
	}
	
	@Test
	public void demo03(){
		// list 只放不取
		Session session = SessionUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		// 存放
		List<Customer>  all = session.createQuery("from Customer").list();
		for (Customer customer : all) {
			System.out.println(customer.getName());
		}
		
		transaction.commit();
		session.close();
		
		Session session2 = SessionUtils.openSession();
		Transaction transaction2 = session2.beginTransaction();
		
		// get获取
		Customer customer2 = (Customer) session2.get(Customer.class, 3); //session 一级缓存失效,从二级缓存获得数据
		System.out.println(customer2);
		
		// list不从缓存获取
		List<Customer>  all2 = session2.createQuery("from Customer").list();
		for (Customer customer : all2) {
			System.out.println(customer.getName());
		}
		
		transaction2.commit();
		session2.close();
		
		
		
		
	}
	
	@Test
	public void demo04(){
		// 集合级别的缓存  -- 将Order类级别缓存注释掉
		Session session = SessionUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer customer = (Customer) session.get(Customer.class, 3); //查询客户
		Set<Order> OrderSet = customer.getOrderSet();
		System.out.println(OrderSet.size());				//查询所有订单
		for(Order order : OrderSet){
			System.out.println(order.getPrice());
		}
		
		transaction.commit();
		session.close();

		Session session2 = SessionUtils.openSession();
		Transaction transaction2 = session2.beginTransaction();
		
		// get获取
		Customer customer2 = (Customer) session2.get(Customer.class, 3); //session 一级缓存失效,从二级缓存获得数据
		Set<Order> orderSet2 = customer2.getOrderSet();
		System.out.println(orderSet2.size());	//没有查询,获得所有的订单
		for(Order order : orderSet2){
			System.out.println(order.getPrice());
		}
		
		transaction2.commit();
		session2.close();
		
		
	}
	
	@Test
	public void demo05(){
		// 时间戳缓存
		Session session = SessionUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Customer customer = (Customer) session.get(Customer.class, 3); //查询客户
		System.out.println(customer.getName());
		// 使用query进行更新
		session.createQuery("update Customer set name = :name where id = :id")
				.setString("name", "rose")
				.setInteger("id", 3)
				.executeUpdate();
		
		transaction.commit();
		session.close();

		Session session2 = SessionUtils.openSession();
		Transaction transaction2 = session2.beginTransaction();
		
		// get获取
		Customer customer2 = (Customer) session2.get(Customer.class, 3); //session 一级缓存失效,从二级缓存获得数据
		System.out.println(customer2.getName());
		
		transaction2.commit();
		session2.close();
		
		
	}
	
	@Test
	public void demo6(){
		// 默认Query 每次都查询 查询缓存
		
		Session session = SessionUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Query query = session.createQuery("from Customer");
		query.setCacheable(true);		//将查询语句 与 结果 缓存到  查询缓存中
		List<Customer> all = query.list();
		System.out.println(all);
		
		transaction.commit();
		session.close();
		
		Session session2 = SessionUtils.openSession();
		Transaction transaction2 = session2.beginTransaction();
		
		Query query2 = session2.createQuery("from Customer");
		query2.setCacheable(true);		//从查询缓存中 获取数据
		List<Customer> all2 = query2.list();
		System.out.println(all2);
		
		transaction2.commit();
		session2.close();
		
		
	}
	
}

Coding Diary

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值