二级缓存是SessionFactory级别的缓存, 是一种进程级别的缓存, 这个缓存可以跨session
生命周期跟SessionFactory周期一样, 如果关闭SessionFactory, 则二级缓存消失
二级缓存跟一级缓存的相同点在于同样只能缓存实体对象而不能缓存属性
适合使用二级缓存:
数据比较少改动(经常被读, 很少修改)
数据不是很重要, 只是偶尔的并发
参考数据
不适合使用二级缓存:
经常要修改的数据
财务数据, 绝对不允许并发问题的出现
和其他的环境的数据做共享的数据
第三方的二级缓存产品: ehcache
支持在内存和硬盘上做存储/支持集群/支持查询缓存
第一步: 引入"ehcache.xml"配置文件: 链接:https://pan.baidu.com/s/1w4KIHdqpdWYkw9sxLQSz9A 密码:0km5
第二步: 开启二级缓存的配置的总开关:
"hibernate.cfg.xml"配置文件:
<!-- 手动开启二级缓存
-->
<property name="hibernate.cache.use_second_level_cache">true</property>
此时开启的是Hibernate框架自己的二级缓存, 我们需要整合ehcache第三方的二级缓存
"hibernate.cfg.xml"配置文件:
<!-- 配置整合ehcache的二级缓存
-->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
ehcache的配置说明:
属性 | 说明 |
maxElementsInMemory | 缓存中对象的个数(缓存的大小) |
eternal | 缓存中的对象是否用久有效(一般都不会设置成永久有效) |
timeToIdleSecond | 对象失效前允许的闲置时间 |
timeToLiveSecond | 对象在缓存中存在的最大时间 |
overFlowToDisk | 如果缓存已满, 则可以写在硬盘上 |
maxElementsOnDisk | 对象在硬盘上的最大数量 |
diskPersisent | 对象在硬盘上是否永久有效(一般不用设置) |
memoryStoreEvictionPolicy | ①LRC(最近最少使用) ②FIFO(先进先出) |
第二步: 对具体的对象开启二级缓存:
"User.hbm.xml"配置文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
package:指定当前映射文件的实体类model所在的包
-->
<hibernate-mapping package="com.rl.hiber.model">
<class name="User" table="t_user">
<!-- 对具体的user对象开启二级缓存
-->
<cache usage="read-only"/>
<!-- id
是主键映射配置
-->
<id name="userId" column="user_id">
<!--
generator:主键的映射策略
-->
<generator class="native"></generator>
</id>
<property name="uname"></property>
<property name="gender"></property>
<property name="birthday"></property>
</class>
</hibernate-mapping>
测试代码:
package com.rl.hiber.test;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.junit.Test;
import com.rl.hiber.model.User;
import com.rl.hiber.utils.HibernateUtils;
public class TestHibernate3 {
/**
* 二级缓存跨session
* get和load方法都能支持二级缓存
*/
@Test
public void query1(){
Session session = HibernateUtils.getSession();
try {
User user = (User)session.get(User.class, 1);
// User user = (User)session.load(User.class, 1);
System.out.println(user);
} finally{
HibernateUtils.closeSession(session);
}
Session session2 = HibernateUtils.getSession();
try {
User user = (User)session2.get(User.class, 1);
// User user = (User)session2.load(User.class, 1);
System.out.println(user);
} finally{
HibernateUtils.closeResource(session2);
}
}
/**
* list不使用二级缓存
*/
@Test
public void query2(){
Session session = HibernateUtils.getSession();
try {
Query query = session.createQuery("from User");
List<User> userList = query.list();
for(User user: userList){
System.out.println(user);
}
} finally{
HibernateUtils.closeSession(session);
}
Session session2 = HibernateUtils.getSession();
try {
Query query = session2.createQuery("from User");
List<User> userList = query.list();
for(User user: userList){
System.out.println(user);
}
} finally{
HibernateUtils.closeResource(session2);
}
}
/**
* iterate方法使用二级缓存
* 先查出所有id, 再根据每个id具体查询
*/
@Test
public void query3(){
Session session = HibernateUtils.getSession();
try {
Query query = session.createQuery("from User");
Iterator<User> iterator = query.iterate();
while(iterator.hasNext()){
User user = iterator.next();
System.out.println(user);
}
} finally{
HibernateUtils.closeSession(session);
}
Session session2 = HibernateUtils.getSession();
try {
Query query = session2.createQuery("from User");
Iterator<User> iterator = query.iterate();
while(iterator.hasNext()){
User user = iterator.next();
System.out.println(user);
}
} finally{
HibernateUtils.closeResource(session2);
}
}
}
二级缓存的策略:
二级缓存的策略有<cache>中的usage属性来控制
read-only: 只读缓存, 二级缓存中的数据无法修改
nonstrict-read-write: 允许更新数据库, 一旦更新则二级缓存中的相对应的数据会被清空, 重新查询的话则需要重新发出sql; 但是新增数据则不会清掉二级缓存的数据, 重新查询不会发出sql也能查询到新增的最新数据
read-write: 允许更新数据库, 一旦更新, 二级缓存的数据也会被同步更新, 重新查询则不需要发出sql也能查询到最新的数据
测试代码:
package com.rl.hiber.test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.User;
import com.rl.hiber.utils.HibernateUtils;
public class TestHibernate4 {
/**
* usage: read-only
*/
@Test
public void query1(){
Session session = HibernateUtils.getSession();
try {
User user = (User)session.get(User.class, 1);
System.out.println(user);
} finally{
HibernateUtils.closeSession(session);
}
Session session2 = HibernateUtils.getSession();
Transaction tx = session2.beginTransaction();
try {
//不发出sql, 从二级缓存中读取数据
User user = (User)session2.get(User.class, 1);
System.out.println(user);
user.setUname("周八");
session2.update(user);
//提交时报错, 因为二级缓存配置为只读, 不允许被修改
tx.commit();
} catch(Exception e){
e.printStackTrace();
tx.rollback();
}finally{
HibernateUtils.closeResource(session2);
}
}
/**
* usage: nonstrict-read-write
*/
@Test
public void query2(){
Session session2 = HibernateUtils.getSession();
Transaction tx = session2.beginTransaction();
try {
//发出查询sql
User user = (User)session2.get(User.class, 1);
System.out.println(user);
user.setUname("周八");
session2.update(user);
//nonstrict-read-write的策略是此时发出修改数据库的sql, 并同步清空二级缓存的user对象
tx.commit();
} catch(Exception e){
e.printStackTrace();
tx.rollback();
}finally{
HibernateUtils.closeSession(session2);
}
Session session = HibernateUtils.getSession();
try {
//由于二级缓存的user对象被清空, 所有必须重新发送sql
User user = (User)session.get(User.class, 1);
//输出查询的数据
System.out.println(user);
} finally{
HibernateUtils.closeResource(session);
}
}
/**
* usage: read-write
*/
@Test
public void query3(){
Session session2 = HibernateUtils.getSession();
Transaction tx = session2.beginTransaction();
try {
//发出查询sql
User user = (User)session2.get(User.class, 1);
System.out.println(user);
user.setUname("陈二");
session2.update(user);
//read-write策略是此时发出修改数据库的sql, 并同步更新二级缓存的user对象
tx.commit();
} catch(Exception e){
e.printStackTrace();
tx.rollback();
}finally{
HibernateUtils.closeSession(session2);
}
Session session = HibernateUtils.getSession();
try {
//不发出sql
User user = (User)session.get(User.class, 1);
//输出更新后的数据
System.out.println(user);
} finally{
HibernateUtils.closeResource(session);
}
}
}
二级缓存的手动管理:
测试代码:
package com.rl.hiber.test;
import org.hibernate.CacheMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;
import com.rl.hiber.model.User;
import com.rl.hiber.utils.HibernateUtils;
public class TestHibernate5 {
@Test
public void query1(){
Session session = HibernateUtils.getSession();
try {
//发出sql
User user = (User)session.get(User.class, 1);
System.out.println(user);
} finally{
HibernateUtils.closeSession(session);
}
//获取SessionFactory对象
SessionFactory sf = HibernateUtils.getSessionFactory();
//将指定的user逐出
sf.getCache().evictEntity(User.class, 1);
Session session2 = HibernateUtils.getSession();
try {
//需要重新发出sql
User user = (User)session2.get(User.class, 1);
System.out.println(user);
} finally{
HibernateUtils.closeResource(session2);
}
}
@Test
public void query2(){
Session session = HibernateUtils.getSession();
try {
//设置session一级缓存的数据只能从二级缓存中读,不能向二级缓存中写
session.setCacheMode(CacheMode.GET);
//发出sql
User user = (User)session.get(User.class, 1);
System.out.println(user);
} finally{
HibernateUtils.closeSession(session);
}
Session session2 = HibernateUtils.getSession();
try {
//由于二级缓存中没有user的数据, 所以只能重新发出sql
User user = (User)session2.get(User.class, 1);
System.out.println(user);
} finally{
HibernateUtils.closeResource(session2);
}
}
}