hibernate提供了两个级别的缓存。
第一个级别:Session级别,属于事务范围,由hibernate管理,一般无须干预。
第二个级别:SessionFactory级别,属于进程范围,是一个可插拔缓存插件,由工厂管理。
原理与分类:
根据对象的ID加载和缓存数据。当执行查询获得的结果集为实体对象的时候,hibernate会把获得的实体对象按照ID存到二级缓存中。
在访问指定对象时,先从一级缓存中找,找到就用,没找到就转到二级缓存中找(需要配置和启用二级缓存),找到就用。没找到就查询数据库,然后放在一级和二级缓存中。
内置缓存:hibernate自带,不可卸载。在hibernate初始化阶段将映射元数据和预定义的SQL语句放在sessionFactory的缓存中,内置缓存是只读的。
外置缓存(二级):可配置的缓存插件。默认不使用,其中的数据是数据库数据的复制。
二级缓存的结构:
分四类:
1、Class Cache Region:类级别缓存,主要用于存储PO(实体)对象。
2、Collection Cache Region:集合级别缓存,用于存储集合数据。
3、Query Cache Region:查询缓存,会缓存一些常用查询语句的查询结果。
4、Update TimesTamps:更新时间戳缓存,存放与查询结果相关的表在进行操作时的时间戳,hibernate通过更新时间戳缓存区判断被缓存的查询结果是否过期。
缓存的并发访问策略:
1、只读型(Read-Only):提供Serializable数据隔离级别,对于从来不修改的数据可以采用。
2、读写型(Read-Write):提供Read Commited数据隔离级别,对于经常读但很少被修改的数据,可以采用,可防止脏读。
3、非严格读写(Nonstrict-read-write):不保证缓存与数据库中数据的一致性,提供Read Uncommited事务隔离级别,对于极少被修改,且允许脏读的数据,可以采用。
4、事务型(Transactional):提供Repeatable Read事务隔离级别,对于经常读但是少更改的数据,可采用,可防止脏读和不可重复读。
1、EHChache:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,对hibernate查询缓存提供支持。
2、OpenSymphony OSCache:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,提供丰富的缓存数据过期策略,并且对hibernate查询缓存提供了支持。
3、SwarmCache :可作为集群范围内的缓存,不支持hibernate查询缓存。
4、JbossCache:可作为集群范围内的缓存,支持hibernate查询缓存。
二级缓存的配置和使用:
以Hibernate5.3.7中EHCache为例:
1、引入EHCache的JAR包:
lib下的optional下ehcache所有jar包:Jar包
2、引入EHCache的配置文件:配置文件
<ehcache>
<diskStore path="java.io.tmpdir"/>(设置缓存数据文件的存储目录)
<defaultCache (设置缓存的默认数据过期策略)
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="sampleCache1"(设置具体的命名缓存的数据过期策略)
maxElementsInMemory="10000"(设置缓存对象的最大数目)
eternal="false"(指定是否永不过期)
timeToIdleSeconds="300"(设置对象处于空闲状态的最大秒数)
timeToLiveSeconds="600"(设置对象处于缓存状态的最大秒数)
overflowToDisk="true"(设置内存溢出时是否将溢出对象写入硬盘)
/>
<cache name="sampleCache2"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/>
</ehcache>
3、启用二级缓存:
在hibernate配置文件中启用,并指定哪些实体类需要二级缓存。
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 指定二级缓存框架EHCache -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<!-- 指定哪些类需要加入二级缓存 -->
<class-cache usage="read-write" class="wan.bean.Customer"/>
<class-cache usage="read-write" class="wan.bean.Order"/>
<!-- 缓存集合 -->
<collection-cache usage="read-only" collection="wan.bean.Customer.orderSet"/>
class-cache与collection-cache元素需要放在mapping元素之后。
4、创建测试类文件:
/**
* 二级缓存测试(从SessionFactory缓存中取单个对象)
*/
@Test
public void test1() {
//加载配置文件
Configuration configuration = new Configuration().configure();
//创建session工厂
SessionFactory factory = configuration.buildSessionFactory();
//第一次
Session session =factory.openSession();
Transaction trans = session.beginTransaction();
Customer c1 = session.get(Customer.class,3);
System.out.println("第一次:"+c1.getName());
trans.commit();
session.close();
System.out.println("====================");
//第二次
Session session1 = factory.openSession();
Transaction trans1 = session1.beginTransaction();
Customer c2 = session1.get(Customer.class,3);
System.out.println("第二次:"+c2.getName());
trans1.commit();
session1.close();
}
/**
* 二级缓存测试(从SessionFactory缓存中取单个对象下的集合)
*/
@Test
public void test2() {
//加载配置文件
Configuration configuration = new Configuration().configure();
//创建session工厂
SessionFactory factory = configuration.buildSessionFactory();
//第一次
Session session =factory.openSession();
Transaction trans = session.beginTransaction();
Customer c1 = session.get(Customer.class,3);
Set<Order> orderSet = c1.getOrderSet();
System.out.println("第一次:"+c1.getName());
for (Order order : orderSet) {
System.out.println(order.getAddress());
}
trans.commit();
session.close();
System.out.println("====================");
//第二次
Session session1 = factory.openSession();
Transaction trans1 = session1.beginTransaction();
Customer c2 = session1.get(Customer.class,3);
Set<Order> orderSet1 = c2.getOrderSet();
System.out.println("第二次:"+c2.getName());
for (Order order : orderSet1) {
System.out.println(order.getAddress());
}
trans1.commit();
session1.close();
}
/**
* 三级缓存测试(从缓存中取单个对象)
*/
@Test
public void test3() {
//加载配置文件
Configuration configuration = new Configuration().configure();
//创建session工厂
SessionFactory factory = configuration.buildSessionFactory();
//第一次
Session session =factory.openSession();
Transaction trans = session.beginTransaction();
Query<Customer> query = session.createQuery("from Customer where id=3",Customer.class);
query.setCacheable(true);
Customer c1 = query.uniqueResult();
System.out.println(c1.getName());
trans.commit();
System.out.println("===================");
//第二次
session =factory.openSession();
Transaction trans1 = session.beginTransaction();
Query<Customer> query1 = session.createQuery("from Customer where id=3",Customer.class);
query1.setCacheable(true);
c1 = query1.uniqueResult();
System.out.println(c1.getName());
trans1.commit();
}