Hibernate之二级缓存详解

前言

在前文中也讲到了缓存机制和Hibernate一级缓存。一级缓存主要是针对session的缓存。只在session中有效,它的缺点也很明显就是应用范围小,缓存的时间短。而Hibernate的二级缓存正好解决了应用范围小等问题。

二级缓存

Hibernate提供了基于应用程序级别的缓存, 可以跨多个session,即不同的session都可以访问缓存数据。 这个缓存也叫二级缓存。如果是新手,可以理解为类似Tomcat中ServletContext对象,通过配置在整个应用中都能访问到它保存的数据。
Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!如果用户想用二级缓存,只需要在hibernate.cfg.xml中配置即可; 不想用,直接移除,不影响代码。
如果用户觉得hibernate提供的框架框架不好用,自己可以换其他的缓存框架或自己实现缓存框架都可以。

Hibernate缓存图解

这里写图片描述

从上图中,可以看出一级缓存是基于session的,而二级缓存在整个应用层面,在数据库和一级缓存之间有一层缓存区,保存二级缓存的数据,不同的session都可访问缓存数据。

使用二级缓存

查看hibernate.properties配置文件,在Hibernate源码中可以查看,那么二级缓存如何配置?

hibernate.properties配置文件给出:

##########################
### Second-level Cache ###
##########################

#hibernate.cache.use_second_level_cache false【二级缓存默认不开启,需要手动开启】
#hibernate.cache.use_query_cache true      【开启查询缓存】

## choose a cache implementation        【二级缓存框架的实现】

#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默认实现
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider

二级缓存策略

Hibernate给出了四种缓存策略,分别是:
<class-cache usage="read-only"/>     放入二级缓存的对象,只读; 
    <class-cache usage="nonstrict-read-write"/>  非严格的读写
    <class-cache usage="read-write"/>    读写; 放入二级缓存的对象可以读、写;
    <class-cache usage="transactional"/>   (基于事务的策略)

集合缓存

如果在查询部门的时候,可以将部门加入到二级缓存中,如果还要将部门关联部门集合也要加入二级缓存中,怎么办呢?
可以通过集合缓存配置如下:

<collection-cache 
usage="read-write" collection="com.nwpu.geek.second_cache.Dept.emps"/>

查询缓存

因为在前文的Hibernate一级缓存中,我们已经知道query.list()方法和iterator()的区别,它默认情况只会放入一级缓存,不会从一级缓存中取!

因此使用查询缓存,可以让list()查询从二级缓存中取!
说了这么多概念了,我们从实际开发来使用二级缓存。

二级缓存,使用步骤

1) 开启二级缓存(必须手动开启)
2)指定缓存框架(缓存插件)
3)指定哪些类需要加入二级缓存(当然不可能全部类都加入)

这里说明哪些可以加入二级缓存数据?
1很少被修改
2不是很重要的数据,允许出现偶尔的并发问题
3不适合放入二级缓存中的数据

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

注意:二级缓存和一级缓存一样,其实底层是基于Map的方式保存缓存数据的,类似于Map<条件,对象>。缓存数据的访问是根据条件key来查找对象。

代码方式实现:

Dept.java

public class Dept {

    private int deptId;
    private String deptName;
    // 【一对多】 部门对应的多个员工
    private Set<Employee> emps = new HashSet<Employee>();


    public Dept(int deptId, String deptName) {
        super();
        this.deptId = deptId;
        this.deptName = deptName;
    }
    public Dept() {
        super();
    }
    public int getDeptId() {
        return deptId;
    }
    public void setDeptId(int deptId) {
        this.deptId = deptId;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    public Set<Employee> getEmps() {
        return emps;
    }
    public void setEmps(Set<Employee> emps) {
        this.emps = emps;
    }
    @Override
    public String toString() {
        return "Dept [deptId=" + deptId + ", deptName=" + deptName + "]";
    }
}

dept映射文件

<hibernate-mapping package="com.nwpu.geek.second_cache">

    <class name="Dept" table="t_dept" >
        <id name="deptId">
            <generator class="native"></generator>
        </id>   
        <property name="deptName" length="20"></property>
         <set name="emps">
             <key column="dept_id"></key>
             <one-to-many class="Employee"/>
         </set>
    </class>

</hibernate-mapping>

这里的Employee.java和配置文件省略了,需要的同学可翻看一级缓存的案例,也是这个代码。由于篇幅的原因,这里就不再赘述了。

hibernate.cfg.xml总配置文件

<hibernate-configuration>
    <!-- 通常,一个session-factory节点代表一个数据库 -->
    <session-factory>

        <!-- 1. 数据库连接配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hib_demo</property>
        <property name="hibernate.connection.username">xxx</property>
        <property name="hibernate.connection.password">xxx</property>
        <!-- 
            数据库方法配置, hibernate在运行的时候,会根据不同的方言生成符合当前数据库语法的sql
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <!-- 2. 其他相关配置 -->
        <!-- 2.1 显示hibernate在运行时候执行的sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 2.2 格式化sql
        <property name="hibernate.format_sql">true</property>  -->
        <!-- 2.3 自动建表  -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- 配置session的创建方式:线程方式创建session对象 -->
        <property name="hibernate.current_session_context_class">thread</property>

        <!--****************** 【连接池配置】****************** -->
        <!-- 配置连接驱动管理类 -->
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
        <!-- 配置连接池参数信息 -->
        <property name="hibernate.c3p0.min_size">2</property>
        <property name="hibernate.c3p0.max_size">4</property>
        <property name="hibernate.c3p0.timeout">5000</property>
        <property name="hibernate.c3p0.max_statements">10</property>
        <property name="hibernate.c3p0.idle_test_period">30000</property>
        <property name="hibernate.c3p0.acquire_increment">2</property>

        <!--****************** 【二级缓存配置】****************** -->
        <!-- a.  开启二级缓存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- b. 指定使用哪一个缓存框架(默认提供的) -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
        <!-- 开启查询缓存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
        <!-- c. 指定哪一些类,需要加入二级缓存 -->
        <class-cache usage="read-write" class="com.nwpu.geeker.second_cache.Dept"/>
        <class-cache usage="read-only" class="com.nwpu.geeker.second_cache.Employee"/>
        <!-- 集合缓存[集合缓存的元素对象,也要加入二级缓存] -->
        <collection-cache usage="read-write" collection="com.nwpu.geeker.second_cache.Dept.emps"/>

    </session-factory>
</hibernate-configuration>

上面包括了数据库连接池的配置,二级缓存的配置。

测试App.java

public class App {

    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Dept.class)   
            .addClass(Employee.class)   // 测试时候使用
            .buildSessionFactory();
    }
    // 1. 测试二级缓存的使用
    // 没有/有用 二级缓存
    @Test
    public void testCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // a. 查询一次
        Dept dept = (Dept) session1.get(Dept.class, 10);
        dept.getEmps().size();// 集合
        session1.getTransaction().commit();
        session1.close();

        System.out.println("------");

        // 第二个session
        Session session2 = sf.openSession();
        session2.beginTransaction();
        // a. 查询一次
        dept = (Dept) session2.get(Dept.class, 10);  // 二级缓存配置好; 这里不查询数据库
        dept.getEmps().size();

        session2.getTransaction().commit();
        session2.close();
    }


    @Test
    public void listCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // HQL查询  【setCacheable  指定从二级缓存找,或者是放入二级缓存】
        Query q = session1.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());
        session1.getTransaction().commit();
        session1.close();


        Session session2 = sf.openSession();
        session2.beginTransaction();
        q = session2.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());  // 不查询数据库: 需要开启查询缓存
        session2.getTransaction().commit();
        session2.close();
    }
}

从上面代码测试来看,第一个测试是测试二级缓存的使用,先创建两个session,第一个session中获取dept和集合emps,因为dept和employee类配置了二级缓存,还配置了集合缓存。因此,在控制台中可以看到只查询一次sql语句,第二次session获取的时候将直接从二级缓存中取数据,不会再执行一次sql.
第二个测试listCache(),主要是测试查询缓存。因为一级缓存中list的查到的数据会放到session缓存中,但是取数据的数据不会从缓存中取,二级缓存可以做到这点。但是必须配置开启查询配置。然后在代码中通过setCacheable(true)来手动放入到二级缓存中。每次查询的时候会先从二级缓存中找,如果找到了,就不用执行sql查询语句了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值