mybatis的一级缓存使用以及禁用

目录

验证代码如下

mappper 代码

xml 中代码

实际执行代码

执行结果

DefaultSqlSession

CachingExecutor

BaseExecutor

PerpetualCache

总结

禁用一级缓存

mapper 对应的 xml 的 select 查询设置 flushCache 属性为 true

MappedStatement 的内部类 Builder 向外部变量 flushCacheRequired 赋值

MapperBuilderAssistant 的 setStatementCache()

XMLStatementBuilder 的 parseStatementNode() 解析 mapper 中定义的 sql

mybatis 全局配置 settings 中添加 name 为 localCacheScope 的节点,对应 value 为 STATEMENT

XMLConfigBuilder的settingsElement()方法

有一个问题,为什么每次创建 SqlSession 缓存就不能共用了?


在之前的文章基础上

https://blog.csdn.net/zlpzlpzyd/article/details/135171524

验证代码如下

mappper 代码

package cn.hahaou.mybatis.cache.levelone.mapper;

import cn.hahaou.mybatis.cache.levelone.entity.Role;

public interface LevelOneRoleMapper {

    Role getRole(Long id);
}

xml 中代码





    
        
        
        
    

    

实际执行代码

package cn.hahaou.mybatis.cache.levelone;

import cn.hahaou.mybatis.cache.levelone.mapper.LevelOneRoleMapper;
import cn.hahaou.util.MybatisUtils;
import org.apache.ibatis.session.SqlSession;

/**
 * 一级缓存测试
 */
public class LevelOneCacheTest {

    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.openSession();
        LevelOneRoleMapper roleMapper = sqlSession.getMapper(LevelOneRoleMapper.class);
        roleMapper.getRole(1L);
        System.out.println("使用同一个SqlSession再执行一次");
        roleMapper.getRole(1L);
        sqlSession.close();
    }
}

执行结果

2023-12-23 19:30:56,109 [main] DEBUG org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
2023-12-23 19:30:56,204 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:30:56,206 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:30:56,206 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:30:56,206 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
2023-12-23 19:31:15,482 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
2023-12-23 19:31:15,943 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 439928219.
2023-12-23 19:31:15,943 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1a38c59b]
2023-12-23 19:31:15,953 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select * from t_role t where t.id = ? 
2023-12-23 19:31:16,082 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
2023-12-23 19:31:16,187 [main] DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
使用同一个SqlSession再执行一次
2023-12-23 19:32:41,371 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1a38c59b]
2023-12-23 19:32:41,378 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1a38c59b]
2023-12-23 19:32:41,379 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 439928219 to pool.

可以看到,最终查询只执行了一次。

在之前的获取 SqlSession 的基础上,有一个地方

mybatis的一级缓存使用以及禁用_第1张图片

这里的 Executor 是在  Configuration 中获取的,这里需要一个参数 ExecutorType,看看调用这里的地方是怎么传参的。

mybatis的一级缓存使用以及禁用_第2张图片

可知,默认是取的  Configuration 的 defaultExecutorType 的值,再到 Configuration 看一下。

由源码得知,Configuration 中 ExecutorType 的值默认为 SIMPLE

mybatis的一级缓存使用以及禁用_第3张图片

 回到刚才创建的地方,最终返回的结果为 CachingExecutor,通过责任链模式的方式包装了 SimpleExecutor。

mybatis的一级缓存使用以及禁用_第4张图片

mybatis的一级缓存使用以及禁用_第5张图片 Executor的实现类

DefaultSqlSession

调用了 mapper 中的方法后,触发反射操作进入 MapperProxy 的 invoke()

mybatis的一级缓存使用以及禁用_第6张图片

鉴于当前查询是单值操作,走查询单条数据逻辑mybatis的一级缓存使用以及禁用_第7张图片

MapperMethod 中有两个类型的变量 SqlCommand 和 MethodSignature。

SqlCommand 里面有两个变量

name 保存了当前访问的 mapper 方法。

type 对应 mapper 里的 sql 标签定义的各种 sql 操作类型,即 dml 操作

MethodSignature

对应 mapper 里的 sql 标签里的各个属性设置

mybatis的一级缓存使用以及禁用_第8张图片

 mybatis的一级缓存使用以及禁用_第9张图片

通过 Configuration 获取 MappedStatement 对象。

MappedStatement 中 sqlSource 的类型是 RawSqlSource,通过责任链模式的方式内嵌了 StaticSqlSource,最终的 sql 在变量 sql 里。

调用 CachingExecutor 的 query()

CachingExecutor

mybatis的一级缓存使用以及禁用_第10张图片

在这里有两件事,创建 CacheKey 和调用嵌套的 SimpleExecutor 执行查询。其中 CacheKey 用于接下来缓存查询结果使用。

BaseExecutor

mybatis的一级缓存使用以及禁用_第11张图片

SimpleExecutor 是 BaseExecutor 的子类。CachingExecutor 中调用了 createCacheKey() 实际上调用了 CacheKey 的 createCacheKey()。

其中主要通过 update() 向里面的集合变量添加数据。具体信息如下

第1个为 MappedStatement 的 id,即需要查询的方法全路径
第2个为 RowBounds 的 offset,默认值为 0
第3个为 RowBounds 的 limit,默认值为 Integer.MAX_VALUE
第4个为 BoundSql 的 sql
第5个为查询的参数值
第6个为 Environment 信息,在配置中指定

定义了一个全局变量 localCache 缓存查询结果

mybatis的一级缓存使用以及禁用_第12张图片

首次查询没有数据,调用 queryFromDatabase() 从数据库中查询。否则,直接从缓存中获取数据。

mybatis的一级缓存使用以及禁用_第13张图片

 查询结束后将 CacheKey 和对应的结果分为作为键值对保存到 localCache 中。

mybatis的一级缓存使用以及禁用_第14张图片

PerpetualCache

mybatis的一级缓存使用以及禁用_第15张图片

数据最终存储到了 map 对象中。

总结

可以看到,通过一个简单的查询,mybatis 使用了责任链模式,通过内存缓存当前查询结果,但是这个只适用于那种单体应用,分布式应用的话使用一级缓存不太好,有缓存不一致的问题。

如果数据量大的话会造成内存溢出的情况发生。

所以,针对项目部署的是集群环境,不要用一级缓存。如果是单体数据量不大可以使用。

禁用一级缓存

mybatis的一级缓存使用以及禁用_第16张图片

可知,有两种方式

mapper 对应的 xml 的 select 查询设置 flushCache 属性为 true

MappedStatement 的内部类 Builder 向外部变量 flushCacheRequired 赋值

mybatis的一级缓存使用以及禁用_第17张图片

MapperBuilderAssistant 的 setStatementCache()

mybatis的一级缓存使用以及禁用_第18张图片

setStatementCache() 的上层调用方法 addMappedStatement()

mybatis的一级缓存使用以及禁用_第19张图片

XMLStatementBuilder 的 parseStatementNode() 解析 mapper 中定义的 sql

mybatis的一级缓存使用以及禁用_第20张图片

可以看到,如果 SqlCommandType 值为 SELECT,flushCache 的值的情况如下

如果 flushCache 的值未设置,flushCache 值为 false,默认使用缓存。

如果 flushCache 的值设置为 true,flushCache 值为 true,禁止使用缓存。

mybatis 全局配置 settings 中添加 name 为 localCacheScope 的节点,对应 value 为 STATEMENT

其中 localCacheScope 实际对应的是枚举类型 LocalCacheScope,只有两个值,默认值为 SESSION。

XMLConfigBuilder的settingsElement()方法

mybatis的一级缓存使用以及禁用_第21张图片

package org.apache.ibatis.session;

/**
 * @author Eduardo Macarron
 */
public enum LocalCacheScope {
  SESSION,STATEMENT
}

如果指定了其他值,创建 SqlSessionFactory 的过程中会出现异常。

这两种方式,要讲哪种方式好,还是全局方式好,当然,对于一级缓存禁用的情况需要按照实际情况来。

有一个问题,为什么每次创建 SqlSession 缓存就不能共用了?

因为每次在调用 SqlSessionFactory 的 openSession() 都会创建 Executor 实例,但是 BaseExecutor 是 SimpleExecutor 的父类,CachingExecutor 通过责任链模式包装了 SimpleExecutor,所以,新建了 SqlSession 就不能使用之前的缓存了。

你可能感兴趣的:(mybatis,缓存,mybatis)