王小码

导航

Mybaits(13)缓存

一、概述

  MyBatis像大多数持久层框架一样,也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。 

  MyBatis分为一级缓存和二级缓存,同时也可以配置关于缓存的设置。

二、一级缓存

 1.介绍

 一级缓存是在SqlSession上的缓存,只要 SqlSession 没有 flush 或 close,它就存在。默认情况下,也就是没有任何配置的情况下,MyBatis系统会开启一级缓存,也就是对于SqlSession层面的缓存,这个缓存不需要POJO对象序列化(实现java.io.Serializable接口)。

 2.测试,证明一级缓存的存在

 (1)编写测试类

 

@Test
    public void  testCache() {
        // 5.创建Dao的代理对象
        roleDao = session.getMapper(IRoleDao.class);
        logger.info("第一次获取...");
        Role role = roleDao.getRole(1L);
        logger.info("第二次获取...");
        Role role1 = roleDao.getRole(1L);

    }

(2)查看日志

 

 

(3)分析

  虽然代码对同一对象进行了两次获取,但是实际上只有一条SQL,其原因是代码使用了同一个SqlSession对象获取数据,当一个SqlSession第一次通过Sql和参数获取对象后,它就将其缓存起来,如果下次Sql和参数都发生变化,并且缓存没有超时或者声明需要刷新时,它就会从缓存中获取数据,而不是通过Sql获取了。

 

   当一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等会清空一级缓存。如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

(4)一级缓存清空测试

清空缓存

@Test
    public void  testClearCache() {
        // 5.创建Dao的代理对象
        roleDao = session.getMapper(IRoleDao.class);
        logger.info("第一次获取...");
        Role role = roleDao.getRole(1L);
        //此方法也可以清空缓存
        session.clearCache();
        logger.info("第二次获取...");
        Role role1 = roleDao.getRole(1L);
        logger.info(role==role1);

    }

 

 从日志可以看出sql执行了两次,最后执行结果为false,缓存被清空。对于不同的SqlSession对象是不能共享的,为了使SqlSession对象之间能共享相同的缓存,有时需要开启二级缓存,开启二级缓存很简单,只需要在映射文件上加入代码:<cache/>,这个时候MyBatis会序列化和反序列对应的实体类(POJO),也就要求POJO是一个可序列化的对象,那么必须实现java.io.Serializable接口。

二、二级缓存

1、概念

  指的是MyBatis中的SqlSessionFactory对象的缓存。由于同一个SqlSessionFactory对象的创建的SQLSession共享其内存。

  二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 

2、二级缓存测试

(1)二级缓存开与关

  在 SqlMapConfig.xml 文件开启二级缓存 

 

<settings>
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="aggressiveLazyLoading" value="false" />
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

 

  因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
false 代表不开启二级缓存。 

(2)配置相关的 Mapper 映射文件

  标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xhbjava.dao.IUserDao">
    <cache></cache>
    <resultMap type="com.xhbjava.domain.User" id="userMapper">

        <id column="id" property="id" />
        <result column="user_name" property="userName" />
        <result column="real_name" property="realName" />
        <result column="sex" property="sex"
            typeHandler="com.xhbjava.typeHandler.SexTypeHandler" />
        <result column="mobile" property="moble" />
        <result column="email" property="email" />
        <result column="position" property="position" />
        <result column="note" property="note" />
        <collection property="roleList" column="id"
            fetchType="lazy" select="com.xhbjava.dao.IRoleDao.findRoleByUserId" />
    </resultMap>
    <select id="getUser" parameterType="long" resultMap="userMapper">
        select id,
        user_name, real_name, sex, moble, email, note from t_user where
        id
        =#{id}
    </select>
    <select id="findUserByRoleId" parameterType="long"
        resultMap="userMapper">
        select u.id, u.user_name, u.real_name, u.sex, u.moble,
        u.email, u.note
        from
        t_user u , t_user_role ur where u.id = ur.user_id
        and ur.role_id =#{roleId}
    </select>

</mapper>

(3)配置 statement 上面的 useCache 属性

<select id="getUser" parameterType="long" resultMap="userMapper" useCache="true">
        select id,
        user_name, real_name, sex, moble, email, note from t_user where
        id
        =#{id}
    </select>

  将 UserDao.xml 映射文件中的<select>标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。

  注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

(4)编写测试类进行测试

@Test
	public void  testSecondLevelCache() {
		// 5.创建Dao的代理对象
		 session = factory.openSession(true);
	     userDao = session.getMapper(IUserDao.class);
		logger.info("第一次获取...");
		User user = userDao.getUser(1L);
		//一级缓存消失
		session.close();
		logger.info("第二次获取...");
		// 5.创建Dao的代理对象
		session11 = factory.openSession(true);
		userDao1 = session11.getMapper(IUserDao.class);
		User user1 = userDao1.getUser(1L);
		logger.info(user==user1);

	}

 

 

  经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。 

注意:使用二级缓存,缓存的类一定要实现 java.io.Serializable 接口,这样可以使用序列化方式来保存对象。 

三、缓存配置项、自定义和引用

1.cache元素配置项:

 

属性 说明 取值 备注
blocking 是否使用阻塞性缓存,在读写时会加入JNI的锁进行操作 true|false,默认false 保证读写安全性,但加锁后性能不佳
readOnly 缓存内容是否只读 true|false,默认false 如果是只读,不会因为多个线程读写造成不一致
eviction

缓存策略:

LRU最近最少使用:移除最长时间不被使用的对象

FIFO先进先出:按照对象进入缓存的顺序移除

SOFT软引用:移除基于垃圾回收器状态和软引用规则对象

WEAK弱引用:积极移除基于垃圾收集器状态和弱引用规则对象

默认false  
flushInterval

这是一个整数,以毫秒为单位,比如1分钟刷新一次,则配置60000.

默认为null,也就是没有刷新时间,只有执行update,inset和delete

时才会刷新。

正整数

超过设置的整数后缓存消失,不再读取缓存,而是执行SQL

取回数据。

type 自定义缓存类。要求实现org.apache.ibatis.cache.Cache 用于自定义缓存类  
size 缓存对象个数 正整数。默认是1024  

2.自定义缓存类

  在实际工作中,我们可以使用Redis,MongoDB或者其他常用的缓存,假设我们在存在Redis的一个缓存实现类RedisCache,我们可以这样配置:

<cache type="XXX.XX.RedisCache">

<property name="host" value="localhost" />

</cache>

这样配置后,MyBatis就会启用缓存,同时调用setHost(String host)方法,去设置配置的内容。

上面的配置时通用的,对于一些语句也需要自定义,比如一些查询不想让它们进行缓存,我们可以如下配置:

<select ... flushCache="false" userCache="true">

<insert ... flushCache="true" >

<update... flushCache="true" >

<delete ... flushCache="true" >

以上是默认配置,我们可以根据需要去修改。flashCache代表是否刷新缓存,对于select、insert、update和delete都是有效的。useCache是select特有的,代表是否启用缓存。

这里都是在一个映射配置文件中配置的,比如IUserDao.xml,其他的映射配置文件不能使用,如果其他映射配置文件需要使用同样的配置,则可以引用缓存的配置:

<cache-ref namespace="XXX.XX.IUserDao"/>这样就可以引用对应映射文件的cache元素的配置了。

UserDao.xml 映射文件中的

posted on 2020-03-01 18:37  王小码  阅读(187)  评论(0编辑  收藏  举报