这一篇主要是讲二级缓存,Hibernate框架入门就在这篇结束了。结束之后会继续写Spring的学习笔记。因为Hibernate单独使用不多,都是整合到Spring中。Spring中会更加详细,操作性更强,这4篇就当铺垫。
二级缓存【掌握】
1 介绍
1.1 缓存
缓存(Cache): 计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘(永久性数据存储源)的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存
缓存:程序<--(内存)-->硬盘
1.2 什么是二级缓存
hibernate 提供缓存机制:一级缓存、二级缓存
一级缓存:session级别缓存,在一次请求中共享数据。
二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。
SessionFactory的缓存两部分: 内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外只读的。不能操作。
外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存hibernate只提供规范(接口),需要第三方实现类。外置缓存有成为二级缓存。
1.3 二级缓存内部结构
- 二级就是由4部分构成
类级别缓存
集合级别缓存
时间戳缓存
查询缓存(二级缓存的第2大部分,三级缓存)
1.4 并发访问策略
- 访问策略:读写型(read-write)、只读型(read-only)
1.5 应用场景
适合放入二级缓存中的数据:
很少被修改
不是很重要的数据, 允许出现偶尔的并发问题不适合放入二级缓存中的数据
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据
1.6 二级缓存提供商
EHCache:
可作为进程(单机)范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持。–支持集群。
OpenSymphony :
可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
SwarmCache:
可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
JBossCache:
可作为集群范围内的缓存, 支持 Hibernate 的查询缓存
2 配置(操作)
1.导入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar
2.开启二级缓存(我要使用二级缓存)
3.确定二级缓存提供商(我要使用哪个二级缓存)
4.确定需要缓存内容
1>配置需要缓存的类
2>配置需要缓存的集合
5.配置ehcache自定义配置文件
具体略过.
3.演示
3.1证明
@Test
public void demo01(){
//1 证明二级缓存存在
// * 修改toString()
// * 如果二级缓存开启,查询3 没有select语句,表示从二级缓存获得的。
// * 将二级缓存关闭,查询3将触发select语句。
Session s1 = factory.openSession();
s1.beginTransaction();
//1 查询id=1 -- 执行select (查询后,将数据存放在一级缓存,之后由一级缓存同步到二级缓存)
Customer c1 = (Customer) s1.get(Customer.class, 1);
System.out.println(c1);
//2 查询id=1 --从一级缓存获取
Customer c2 = (Customer) s1.get(Customer.class, 1);
System.out.println(c2);
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//3 查询id=1 -- 从二级缓存获取
Customer c3 = (Customer) s2.get(Customer.class, 1);
System.out.println(c3);
s2.getTransaction().commit();
s2.close();
}
3.2 类缓存
类缓存:只存放数据
一级缓存:存放对象本身
public void demo02(){
//2 类缓存:只存放数据,散装数据。
// * 使用默认的toString();
Session s1 = factory.openSession();
s1.beginTransaction();
//1 查询id=1 -- 执行select
Customer c1 = (Customer) s1.get(Customer.class, 1);
System.out.println(c1);
//2 查询id=1 -- 从一级缓存获取,一级缓存存放对象本身
Customer c2 = (Customer) s1.get(Customer.class, 1);
System.out.println(c2);
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//3 查询id=1 -- 对象不一样,数据一样
Customer c3 = (Customer) s2.get(Customer.class, 1);
System.out.println(c3);
s2.getTransaction().commit();
s2.close();
}
3.3 集合缓存
@Test
public void demo03(){
//3 集合缓存:只存放关联对象OID的值,如果需要数据,从类缓存中获取。
// * 3.1 默认:第一条select 查询客户,第二天 select 查询客户所有订单
// * 3.2 操作:在hibernate.cfg.xml 将 Order 类缓存删除
// *** <!-- <class-cache usage="read-write" class="com.itheima.a_init.Order"/>-->
// *** 多了10条select,通过订单的id查询订单
Session s1 = factory.openSession();
s1.beginTransaction();
//1 查询id=1
Customer c1 = (Customer) s1.get(Customer.class, 1);
System.out.println(c1);
//2 获得订单
for (Order o1 : c1.getOrderSet()) {
System.out.println(o1);
}
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//3 查询id=1
Customer c3 = (Customer) s2.get(Customer.class, 1);
System.out.println(c3);
//4 获得订单
for (Order o2 : c3.getOrderSet()) {
System.out.println(o2);
}
s2.getTransaction().commit();
s2.close();
}
3.4 时间戳
@Test
public void demo04(){
//4 时间戳: 所有的操作都会在时间戳中进行记录,如果数据不一致,将触发select语句进行查询
// * 修改toString()
Session s1 = factory.openSession();
s1.beginTransaction();
//1 查询id=1
Integer cid = 1;
Customer c1 = (Customer) s1.get(Customer.class, cid);
System.out.println(c1);
//2 绕过一级和二级缓存,修改数据库,修改客户cname=大东哥
s1.createQuery("update Customer set cname = ? where cid = ?")
.setString(0, "大东哥")
.setInteger(1, cid)
.executeUpdate();
//3打印
System.out.println(c1);
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//4 查询id=1 -- ?
Customer c3 = (Customer) s2.get(Customer.class, 1);
System.out.println(c3);
s2.getTransaction().commit();
s2.close();
}
3.5 查询缓存
查询缓存又称为三级缓存(民间)
查询缓存默认不使用。需要手动开启
- 查询缓存:将HQL语句与 查询结果进行绑定。通过HQL相同语句可以缓存内容。
默认情况Query对象只将查询结果存放在一级和二级缓存,不从一级或二级缓存获取。 查询缓存就是让Query可以从二级缓存获得内容。
步骤一:开启查询缓存
<!-- 9.4 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
步骤二:在查询query对象,设置缓存内容(注意:存放和查询 都需要设置)
@Test
public void demo05(){
//5 查询缓存
Session s1 = factory.openSession();
s1.beginTransaction();
//1 query查询
Query q1 = s1.createQuery("from Customer");
q1.setCacheable(true);
List<Customer> a1 = q1.list();
for (Customer c1 : a1) {
System.out.println(c1);
}
//2 cid =1 -- 一级缓存获得
Customer customer = (Customer) s1.get(Customer.class, 1);
System.out.println(customer);
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//2 cid =1 -- 二级缓存获得
Customer customer2 = (Customer) s2.get(Customer.class, 1);
System.out.println(customer2);
//3 query查询
Query q2 = s2.createQuery("from Customer");
q2.setCacheable(true);
List<Customer> a2 = q2.list();
for (Customer c2 : a2) {
System.out.println(c2);
}
s2.getTransaction().commit();
s2.close();
}
3.4 ehcache配置文件
<diskStore path="java.io.tmpdir"/>
设置临时文件存放位置。(缓存一般内存,一定程度时,写入硬盘。)
- 缓存详细设置
<defaultCache> 所有的缓存对象默认的配置
<cache name="类"> 指定对象单独配置
- 参数设置
maxElementsInMemory="10000" 内存最大数
eternal="false" 是否永久(内存常驻留)
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true" 内存满了,是否写入到硬盘
maxElementsOnDisk="10000000" 硬盘最大数
diskPersistent="false" 关闭JVM,是否将内存保存硬盘中
diskExpiryThreadIntervalSeconds="120" 轮询
memoryStoreEvictionPolicy="LRU"