MyBatis一级缓存和二级缓存

“花明月暗笼轻雾,今宵好向郎边去”

前言

在实际项目开发中,通常对数据库查询的性能要求很高,而 Mybatis提供了查询缓存来缓存数据,从而达到提高查询性能的要求。 Mybatis的査询缓存分为一级缓存和二级缓存。一级缓存是 SqlSession级別的缓存,二级缓存是mapper级别的缓存,二级缓存是多个 SqlSession共享的。 Mybatis通过缓存机制减轻数据压力,提高数据库性能。

  • 一级缓存( SqlSession级別)
    Mybatis的一级缓存是 SqlSession级别的缓存。在操作数据库时需要构造 SqlSession对象,在对象中有一个 HashMap用于存储缓存数据。不同的 SqlSession之间的缓存数据区域( Hashmap)是互相不影响的。
    一级缓存的作用域是 SqlSession范围的,当在同一个 SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次査询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。需要注意的是,如果 SqlSession执行了DML操作( Insert、update和delete),并提交到数据库, MyBatis 则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。
    当一个 SqlSession结束后该 SqlSession中的一级缓存也就不存在了。 Mybatis默认开启一级缓存,不需要进行任何配置。
  • 二级缓存(mapper级别)
    二级缓存是mapper级别的缓存。使用二级缓存时,多个 SqlSession使用同一个 Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用 HashMap进行数据存储。相比一级缓存 SqlSession,二级缓存的范围更大,多个 SqlSession可以共用二级缓存,二级缓存是跨 SqlSession的。
    二级缓存是多个 SqlSession共享的,其作用域是 mapper的同一个 namespace。不同的SqlSession两次执行相同的 namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中査询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
    MyBatis默认没有开启二级缓存,需要在 setting全局参数中配置开启二级缓存。
    注意:
    Mybatis的缓存机制是基于id进行缓存的,也就是说, Mybatis使用 HashMap缓存数据时,是使用对象的id作为key,而对象作为 value保存的。

实例

  • 测试一级缓存
    一级缓存时MyBatis的默认缓存,所以无需进行其缓存的配置。

UserMapper.xml

<?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="org.arunner.mapper.UserMapper">
	
	<!--根据ID查询User-->
	<select id="selectUserById" parameterType="int" resultType="org.arunner.domain.User">
		select * from tb_user where id = #{id} ;
	</select>

	<!--查询所有user-->
	<select id="selectAllUser" resultType="org.arunner.domain.User">
		select * from tb_user ;
	</select>

	<!--根据ID删除User-->
	<delete id="deleteUserById" parameterType="int">
		delete from tb_user where id = #{id};
	</delete>

</mapper>

测试类:

import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;

/**
 * @author arunner
 * @date 2018/11/10
 */
public class TestOneLevelCache {
    public static void main(String[] args) {
        TestOneLevelCache testOneLevelCache = new TestOneLevelCache();
        testOneLevelCache.testCache1();
    }

    //一级缓存:也就是Session级的缓存(默认开启)
    public void testCache1() {
        SqlSession sqlSession = FKSqlSessionFactory.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //查询ID为1的用户
        User user = mapper.selectUserById(1);
        System.out.println(user);
        //再次查询ID为1的对象,因为是同一个sqlSession,所以会从之前的一级缓存中查找数据
        User user1 = mapper.selectUserById(1);
        System.out.println(user1);
        //关闭sqlSession对象
        sqlSession.close();
    }

}

在这里插入图片描述

第二次测试(执行commit操作):

import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;

/**
 * @author arunner
 * @date 2018/11/10
 */
public class TestOneLevelCache {
    public static void main(String[] args) {
        TestOneLevelCache testOneLevelCache = new TestOneLevelCache();
        testOneLevelCache.testCache2();
    }


    public void testCache2() {
        SqlSession sqlSession = FKSqlSessionFactory.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //查询ID为1的用户
        User user = mapper.selectUserById(1);
        System.out.println(user);
        //执行delete操作
        mapper.deleteUserById(5);
        //commit提交
        sqlSession.commit();
        //再次查询id为1的User对象,因为DML操作会清空SqlSession缓存,所以会再次执行select语句
        User user1 = mapper.selectUserById(1);
        System.out.println(user1);
        //关闭sqlSession对象
        sqlSession.close();
    }
}

在这里插入图片描述

  • 测试二级缓存
    因为二级缓存不是MyBatis的默认方式,所以需要手动配置:
  1. mybatis-config.xml 文件中设置缓存机制:

在这里插入图片描述

  1. 在需要缓存的mapper中设置:
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

在这里插入图片描述
这里的几个属性说明一下:
以上配置创建了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而且返回的对象被认为是只读的。
cache元素用来开启当前 mapper的 namespace下的二级缓存。该元素的属性设置如下:
flushInterval:刷新间隔。可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况下是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size:缓存数目。可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
readOnly:只读。属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
eviction:收回策略,默认为LRU。有如下几种:

  • LRU。最近最少使用的策略,移除最长时间不被使用的对象
  • FIFO。先进先出策略,按对象进入缓存的顺序来移除它们
  • SOFT。软引用策略,移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK。弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。

测试二级缓存
执行删除操作,测试二级缓存:

import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;

/**
 * @author arunner
 * @date 2018/11/10
 */
public class TestTwoLevelCache {
    public static void main(String[] args) {
        TestTwoLevelCache testTwoLevelCache = new TestTwoLevelCache();
        testTwoLevelCache.testCache1();
    }

    public void testCache1() {
        // 使用工厂类获得SqlSession对象
        SqlSession session = FKSqlSessionFactory.getSqlSession();
        // 获得UserMapping对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        // 查询id为1的User对象,会执行select语句
        User user = mapper.selectUserById(1);
        System.out.println(user);
        // 执行delete操作
        mapper.deleteUserById(5);
        // commit提交
        session.commit();
        // 再次查询id为1的User对象,因为DML操作会清空SqlSession缓存,所以会再次执行select语句
        User user2 = mapper.selectUserById(1);
        System.out.println(user2);
        // 关闭SqlSession对象
        session.close();
    }
}

在这里插入图片描述

关闭SqlSession后再次查询,测试二级缓存:

import org.apache.ibatis.session.SqlSession;
import org.arunner.domain.User;
import org.arunner.factory.FKSqlSessionFactory;
import org.arunner.mapper.UserMapper;

/**
 * @author arunner
 * @date 2018/11/10
 */
public class TestTwoLevelCache {
    public static void main(String[] args) {
        TestTwoLevelCache testTwoLevelCache = new TestTwoLevelCache();
        testTwoLevelCache.testCache2();
    }

    public void testCache2() {
        SqlSession sqlSession = FKSqlSessionFactory.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //查询ID为1的用户
        User user = mapper.selectUserById(1);
        System.out.println(user);
        //关闭一级缓存
        sqlSession.close();
        //再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常
        SqlSession sqlSession1 = FKSqlSessionFactory.getSqlSession();
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        //查询ID为1的用户
        User user1 = mapper1.selectUserById(1);
        System.out.println(user1);
        //关闭sqlSession对象
        sqlSession.close();
    }
}

在这里插入图片描述

当然,如果你也可以为某个查询设置每次都查数据库,不从缓存中获取。设置方法如下:

useCache="false"在这里插入图片描述

  • 10
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值