1.Hibernate的cache介绍:
Hibernate实现了良好的Cache机制,可以借助Hibernate内部的Cache迅速提高系统的数据读取性能。Hibernate中的Cache可分为两层:一级Cache和二级Cache。
第一级别的缓存是Session级别的缓存,是属于事务范围的缓存,由Hibernate管理,一般无需进行干预。
二级缓存是属于SessionFactory级别的缓存机制。第二级别的缓存是SessionFactory级别的缓存,是属于进程范围的缓存。跨多个session的缓存,mybatis的二级缓存是跨多个SqlSession的缓存。
一级缓存:
Hibernate默认是开启一级缓存的,一级缓存存放在session上,属于事务级数据缓冲。
对象分为三种状态:瞬时状态、持久化状态、游离状态.其实我们调用session.save或者session.update或者session.saveOrUpdate只是为了将对象的状态改变为持久态(将对象存入session一级缓存)。一级缓存
中的对象就是和session关联,session中有一级缓存区和快照区,执行事务提交的时候会判断快照中对象和缓存中对应的对象是否一致,如果一致不会执行修改SQL、不一致会执行修改SQL。
二级缓存
在SessionFactory,所有的Session共享同一个二级Cache。二级Cache的内部如何实现并不重要,重要的是采用哪种正确的缓存策略,以及采用哪个Cache提供器。
2.二级缓存分类:
二级缓存也分为了两种
内置缓存:Hibernate自带的,不可卸载,通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放置到SessionFactory的缓存中。该内置缓存是只读的。
外置缓存:通常说的二级缓存也就是外置缓存,在默认情况下SessionFactory不会启用这个缓存插件,外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或者硬盘
3.并发访问策略
我们一般用的缓存策略是read-only或者是read-write。
transactional (事务型) |
仅在受管理的环境中适用 提供Repeatable Read事务隔离级别 适用经常被读,很少修改的数据 可以防止脏读和不可重复读的并发问题 缓存支持事务,发生异常的时候,缓存也能够回滚 |
read-write (读写型) |
提供Read Committed事务隔离级别 在非集群的环境中适用 适用经常被读,很少修改的数据 可以防止脏读 更新缓存的时候会锁定缓存中的数据 |
nonstrict-read-write (非严格读写型) |
适用极少被修改,偶尔允许脏读的数据(两个事务同时修改数据的情况很少见) 不保证缓存和数据库中数据的一致性 为缓存数据设置很短的过期时间,从而尽量避免脏读 不锁定缓存中的数据 |
read-only (只读型) |
适用从来不会被修改的数据(如参考数据) 在此模式下,如果对数据进行更新操作,会有异常 事务隔离级别低,并发性能高 在集群环境中也能完美运作 |
适合放入缓存的数据:
很少被修改
不是很重要的数据,允许出现偶尔的并发问题
不适合放入二级缓存中的数据:
经常被修改
财务数据,绝对不允许出现并发问题
与其他应用数据共享的数据
4.在Hibernate中使用EhCache:
1.导包:
hibernate5需要引入slf4j的包,否则会报错。。。。。。。。。。。。。。。。。
2.hibernate.cfg.xml中开启二级缓存:(在hbm文件中配置)
<?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> <!--
#hibernate.dialect org.hibernate.dialect.MySQLDialect
#hibernate.dialect org.hibernate.dialect.OracleDialect
#hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect
#hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect
#hibernate.connection.driver_class com.mysql.jdbc.Driver
#hibernate.connection.url jdbc:mysql:///test
#hibernate.connection.username gavin
#hibernate.connection.password
-->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password">123456</property> <!-- <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
<property name="hibernate.connection.username">sbgl</property>
<property name="hibernate.connection.password">sbgl</property> -->
<!-- 数据库方言
不同的数据库中,sql语法略有区别. 指定方言可以让hibernate框架在生成sql语句时.针对数据库的方言生成.
sql99标准: DDL 定义语言 库表的增删改查
DCL 控制语言 事务 权限
DML 操纵语言 增删改查
注意: MYSQL在选择方言时,请选择最短的方言.
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 使用二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!--设置缓存的类型,设置缓存的提供商 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property> <!-- #hibernate.show_sql true
#hibernate.format_sql true
-->
<!-- 将hibernate生成的sql语句打印到控制台 -->
<property name="hibernate.show_sql">true</property>
<!-- 将hibernate生成的sql语句格式化(语法缩进) -->
<property name="hibernate.format_sql">true</property>
<!--
## auto schema export 自动导出表结构. 自动建表
#hibernate.hbm2ddl.auto create 自动建表.每次框架运行都会创建新的表.以前表将会被覆盖,表数据会丢失.(开发环境中测试使用)
#hibernate.hbm2ddl.auto create-drop 自动建表.每次框架运行结束都会将所有表删除.(开发环境中测试使用)
#hibernate.hbm2ddl.auto update(推荐使用) 自动生成表.如果已经存在不会再生成.如果表有变动.自动更新表(不会删除任何数据).
#hibernate.hbm2ddl.auto validate 校验.不自动生成表.每次启动会校验数据库中表是否正确.校验失败.
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 引入orm元数据
路径书写: 填写src下的路径
-->
<mapping resource="cn/qlq/domain/Customer.hbm.xml" />
<mapping resource="cn/qlq/domain/LinkMan.hbm.xml" />
<mapping resource="cn/qlq/domain/User.hbm.xml" />
<mapping resource="cn/qlq/domain/Role.hbm.xml" />
<mapping resource="cn/qlq/domain/TestType.hbm.xml" /> </session-factory>
</hibernate-configuration>
上面开启是hibernate5开启二级缓存,下面是hibernate4开启二级缓存:
<!--但是在 hibernate 4中设置缓存提供商时是这样设置的 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_query_cache">true</property>
3.导入encache.xml(放在classpath目录下)
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
</ehcache>
4.Customer.hbm.xml中开启二级缓存配置(亲测有效)
为了保险起见 我们需要给相对应的model 实现一个序列化接口 implements Serializable
或者在cfg中:(自己测试的时候报错)
<!-- 缓存对象 -->
<class-cache usage="read-write" class="cn.qlq.domain.Customer"/>
<!-- 缓存集合 -->
<collection-cache usage="read-only" collection="cn.qlq.domain.Customer.linkMens"/>
上面配置方式二选一,我选择在hbm文件中开启二级缓存。
5.测试二级查询缓存
HibernateUtil,java工具类(开启session与开启与线程绑定的session)
package cn.qlq.util; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry; public class HibernateUtil { private static SessionFactory sessionFactory; // 创建一个对象,一个web项目只有一个SessionFactory
static {
// 3.3以及之前的版本构建会话工厂对象
// SessionFactory sessionFactory = new
// Configuration().configure().buildSessionFactory(); // 5.0之后获取SessionFactory
// 创建服务注册对象
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build();
// 创建会话工厂对象
sessionFactory = new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory();
} // 获得session => 获得全新session
public static Session openSession() {
return sessionFactory.openSession();
} // 获得session => 获得与线程绑定的session
public static Session getCurrentSession() {
return sessionFactory.getCurrentSession();
} /**
* 测试方法
*
* @param args
*/
public static void main(String[] args) {
System.out.println(HibernateUtil.openSession());
} }
测试二级缓存:
package cn.qlq.secondaryCache; import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test; import cn.qlq.domain.Customer;
import cn.qlq.util.HibernateUtil; /**
* 测试二级缓存
*
* @author liqiang
*
*/
public class SecondaryCache {
@Test
public void test1() {
// 1.打开两个session(不是与线程绑定的session)
Session session1 = HibernateUtil.openSession();
Transaction tx1 = session1.beginTransaction();
Customer customer = session1.get(Customer.class, 15l);
System.out.println(customer);
tx1.commit();
session1.close();
System.out.println("--------华丽的分割线----------"); Session session2 = HibernateUtil.openSession();
Transaction tx2 = session2.beginTransaction();
Customer customer2 = session2.get(Customer.class, 15l);
System.out.println(customer2);
tx2.commit();
session2.close();
}
}
结果:
只在第一次查询的时候发出SQL请求,第二次获取的时候不会发出SQL请求。证明二级缓存生效。
Hibernate:
select
customer0_.cust_id as cust_id1_0_0_,
customer0_.cust_name as cust_nam2_0_0_,
customer0_.cust_source as cust_sou3_0_0_,
customer0_.cust_industry as cust_ind4_0_0_,
customer0_.cust_level as cust_lev5_0_0_,
customer0_.cust_linkman as cust_lin6_0_0_,
customer0_.cust_phone as cust_pho7_0_0_,
customer0_.cust_mobile as cust_mob8_0_0_
from
cst_customer customer0_
where
customer0_.cust_id=?
Customer [cust_id=15, cust_name=ttt]
--------华丽的分割线----------
Customer [cust_id=15, cust_name=ttt]
6.测试缓存策略:
继续使用上面的缓存策略(read-only),我们进行修改操作:
package cn.qlq.secondaryCache; import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test; import cn.qlq.domain.Customer;
import cn.qlq.util.HibernateUtil; /**
* 测试二级缓存
*
* @author liqiang
*
*/
public class SecondaryCache {
@Test
public void test1() {
// 1.打开两个session(不是与线程绑定的session)
Session session1 = HibernateUtil.openSession();
Transaction tx1 = session1.beginTransaction();
Customer customer = session1.get(Customer.class, 15l);
customer.setCust_name("xxxxx");
tx1.commit();
session1.close();
System.out.println("--------华丽的分割线----------");
}
}
报错:
修改缓存策略为read-write,代码还是上面的修改的代码继续进行访问:
结果正常打印日志且修改数据库:
Hibernate:
select
customer0_.cust_id as cust_id1_0_0_,
customer0_.cust_name as cust_nam2_0_0_,
customer0_.cust_source as cust_sou3_0_0_,
customer0_.cust_industry as cust_ind4_0_0_,
customer0_.cust_level as cust_lev5_0_0_,
customer0_.cust_linkman as cust_lin6_0_0_,
customer0_.cust_phone as cust_pho7_0_0_,
customer0_.cust_mobile as cust_mob8_0_0_
from
cst_customer customer0_
where
customer0_.cust_id=?
Hibernate:
update
cst_customer
set
cust_name=?,
cust_source=?,
cust_industry=?,
cust_level=?,
cust_linkman=?,
cust_phone=?,
cust_mobile=?
where
cust_id=?
--------华丽的分割线----------
接下来还会演技hibernate整合redis进行二级缓存,与mybatis一样进行整合redis进行二级缓存。。。。。。。。。。。。。。。。。(未完待续)
参考:https://blog.csdn.net/luckyzhoustar/article/details/47748179