Mybatis 学习总结10 mybatis的缓存机制


前文引用 https://blog.csdn.net/lovezhaohaimig/article/details/80398568
讲述个人认为比较详细

缓存的概念

当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存(内存,高速磁盘)中.当下次用户再次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询.
减少网络连接和数据库查询带来的损耗,从而提高我们的查询效率,减少高并发访问带来的系统性能问题.

什么是Mybatis缓存

在这里插入图片描述
使用缓存可以减少Java Application与数据库的交互次数,从而提升程序的运行效率。比如,查询id=1的user对象,第一次查询出来之后,会自动将该对象保存到缓存中。下一次查询该对象时,就可以直接从缓存中获取,不需要发送SQL查询数据库了。

Mybatis缓存分类

一级缓存:SqlSession级别,默认开启,且不能关闭。

mybatis的一级缓存是SqlSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间缓存数据区域(HashMap)是互相不影响的。

一级缓存的作用域是SqlSession范围的,当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存)中,第二次查询时会从缓存中获取数据,不再去底层进行数据库查询,从而提高了查询效率。需要注意的是:如果SqlSession执行了DML操作(insert、update、delete),并执行commit()操作,mybatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存数据中存储的是最新的信息,避免出现脏读现象。

当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了,Mybatis默认开启一级缓存,不需要进行任何配置。

二级缓存:Mapper级别,默认关闭,可以手动开启。

二级缓存是Mapper级别的缓存,使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMapper进行数据存储,相比一级缓存SqlSession,二级缓存的范围更大,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的SqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
在这里插入图片描述

代码自测(以下为本人自测,也可以查看上文连接)

测试代码准备
sys_country 库表
在这里插入图片描述
CountryBO 实体类

public class CountryBO implements Serializable {

    private Long id;

    private String countryName;

    private Date addTime;

    private Date modTime;
    
    ......

mapper.xml

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="mapping.CountryMapper">
    <select id="getCountryById" resultType="Model.CountryBO">
        SELECT * FROM sys_country WHERE id = #{id}
    </select>
</mapper>

CountryMapper mapper接口

public interface CountryMapper extends BaseMapper {
    CountryBO getCountryById(Long id);
}
一级缓存(scope :SqlSession)

我们首先编写一个测试类,测试简单的调用两次查询方法

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = Common.getSqlSession(false);
        CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);

        CountryBO ct1 = countryMapper.getCountryById(1L);
        System.out.println(ct1);
        
        CountryBO ct2 = countryMapper.getCountryById(1L);
        System.out.println(ct2);

    }

执行debug,查看SqlSession实例,可以看到有一个localCacje对象,这个就是本地缓存的对象。
在这里插入图片描述
mybatis的本地缓存基于PerpetualCache类,源码如下

public class PerpetualCache implements Cache {
    private final String id;
    private Map<Object, Object> cache = new HashMap();

    public PerpetualCache(String id) {
        this.id = id;
    }

    public String getId() {
        return this.id;
    }

    public int getSize() {
        return this.cache.size();
    }

    public void putObject(Object key, Object value) {
        this.cache.put(key, value);
    }

    public Object getObject(Object key) {
        return this.cache.get(key);
    }

    public Object removeObject(Object key) {
        return this.cache.remove(key);
    }

    public void clear() {
        this.cache.clear();
    }

    public ReadWriteLock getReadWriteLock() {
        return null;
    }

    public boolean equals(Object o) {
        if (this.getId() == null) {
            throw new CacheException("Cache instances require an ID.");
        } else if (this == o) {
            return true;
        } else if (!(o instanceof Cache)) {
            return false;
        } else {
            Cache otherCache = (Cache)o;
            return this.getId().equals(otherCache.getId());
        }
    }

    public int hashCode() {
        if (this.getId() == null) {
            throw new CacheException("Cache instances require an ID.");
        } else {
            return this.getId().hashCode();
        }
    }
}

可以看到mybatis的缓存是基于PerpetualCache 的以键值对的形式进行存储。具体的缓存生成流程,可以跟随源码解读,不多赘述。
直接看执行结果
在这里插入图片描述
我们发现执行了两次查询,只和数据库进行了一次交互。说明第二次是从缓存中读取的。
我们改一下代码,在第一次执行结束后清空sesion的缓存。再测试一下。

SqlSession sqlSession = Common.getSqlSession(false);
        CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);

        CountryBO ct1 = countryMapper.getCountryById(1L);
        System.out.println(ct1);

        sqlSession.clearCache();

        CountryBO ct2 = countryMapper.getCountryById(1L);
        System.out.println(ct2);

在清空之前查看一下SqlSession实例,
在这里插入图片描述
可以看到 key 值 为 类名+方法 + 查询语句 +参数组成的字符串。键值则存储了查询结果。
查询结果
在这里插入图片描述
可以看到这里执行了两次数据读取。返回两条结果。

二级缓存(scope:mapper)

二级缓存仅需要在想要配置二级缓存的mapper.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="mapping.CountryMapper">
	<!--开启二级缓存 -->
    <cache/>
    <select id="getCountryById" resultType="Model.CountryBO">
        SELECT * FROM sys_country WHERE id = #{id}
    </select>
</mapper>

然后在配置文件中 设置 cacheEnabled 为 ture ,实际上mybatis默认这个配置就是true 我们还是展示一下

	<settings>
        <!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。  默认开启-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

需要注意,二级缓存的持久层交互对象需要实现Serializable接口,否则报如下异常

rror committing transaction.  Cause: org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException:

我们在执行一下刚刚的测试代码
在这里插入图片描述
可以从日志中看到,二级缓存生效了。即使我们在第一次执行后清空了一级缓存,但是仍然可以从二级缓存中取到同样查询语句的结果。
我们在测试一下

SqlSession sqlSession = Common.getSqlSession(false);
        CountryMapper countryMapper = sqlSession.getMapper(CountryMapper.class);

        CountryBO ct1 = countryMapper.getCountryById(1L);
        System.out.println(ct1);

        sqlSession.clearCache();

        CountryBO ct2 = countryMapper.getCountryById(1L);
        System.out.println(ct2);

        sqlSession.commit();

        CountryBO ct3 = countryMapper.getCountryById(1L);
        System.out.println(ct3);

结果
在这里插入图片描述
可以看到,二级缓存依然生效了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值