Mybatis一级缓存及其存在的问题

概要:默认情况下,MyBatis只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。这也就是大家常说的MyBatis一级缓存,一级缓存的作用域是SqlSession。

  • 作用域
    取值有两个:SqlSessionStatement

    文档:https://mybatis.org/mybatis-3/configuration.html#settings

    Mybatis plus 基于springboot的配置为:

    mybatis-plus:
      configuration:
        local-cache-scope: statement
    

    或者

    mybatis-plus:
      configuration:
        local-cache-scope: session
    
  • SqlSession
    每个SqlSession中持有了Executor,每个Executor中有一个LocalCache
    执行SQL语句的过程中,首次执行它时从数据库获取的所有数据会被存储在一段高速缓存中,今后执行这条语句时就会从高速缓存中读取结果,而不是再次查询数据库。MyBatis提供了默认下基于HashMap的缓存实现。
    在这里插入图片描述
    类图如下:
    在这里插入图片描述

测试

  • SqlSessionConfig

    package com.example.demo.config;
    
    import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
    import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
    
    import org.apache.ibatis.plugin.Interceptor;
    import org.mybatis.spring.mapper.MapperScannerConfigurer;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import javax.sql.DataSource;
    
    /**
     * @author liuteng
     */
    @Configuration
    public class SqlSessionConfig {
    
        private Logger logger = LoggerFactory.getLogger(SqlSessionConfig.class);
    
        @Bean("sqlSessionFactoryBean")
        public MybatisSqlSessionFactoryBean createSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource,
                                                                    @Qualifier("paginationInterceptor") PaginationInterceptor paginationInterceptor) {
    
            // MybatisSqlSessionFactory
            MybatisSqlSessionFactoryBean sqlSessionFactoryBean = null;
            try {
                // 实例SessionFactory
                sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
                // 配置数据源
                sqlSessionFactoryBean.setDataSource(dataSource);
                // 设置 MyBatis-Plus 分页插件
                Interceptor [] plugins = {paginationInterceptor};
                sqlSessionFactoryBean.setPlugins(plugins);
                // 加载MyBatis配置文件
                PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
                sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath*:mapper/*.xml"));
            } catch (Exception e) {
                logger.error("创建SqlSession连接工厂错误:{}", e.getMessage());
            }
            return sqlSessionFactoryBean;
        }
    
        @Bean("dataSource")
        @ConfigurationProperties(prefix = "spring.datasource")
        public DataSource dataSource(){
            return DataSourceBuilder.create().build();
        }
    
        @Bean
        public MapperScannerConfigurer myGetMapperScannerConfigurer() {
            MapperScannerConfigurer myMapperScannerConfigurer = new MapperScannerConfigurer();
            myMapperScannerConfigurer.setBasePackage("com.example.demo.mapper");
            myMapperScannerConfigurer.setSqlSessionFactoryBeanName("mySqlSessionFactoryBean");
            return myMapperScannerConfigurer;
        }
    
    }
    
  • UserServiceImpl

    package com.example.demo.service.impl;
    
    import com.alibaba.fastjson.JSON;
    import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
    import com.example.demo.mapper.User1Mapper;
    import com.example.demo.mapper.UserMapper;
    import com.example.demo.entity.User;
    import com.example.demo.service.UserService;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.session.SqlSession;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    
    @Slf4j
    @Service
    public class UserServiceImpl implements UserService {
    
    
        @Resource
    	DefaultSqlSessionFactory sqlSessionFactoryBean;
        
        @Override
        public int updateById(User user) {
            try {
                SqlSession sqlSession1 = sqlSessionFactoryBean.openSession();
            	SqlSession sqlSession2 = sqlSessionFactoryBean.openSession();
                UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
                UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    
                User firstUser = userMapper1.selectById(user.getId());
                log.debug("=========SqlSession1查询======== User:{}", JSON.toJSONString(firstUser));
                userMapper2.updateById(user);
                log.debug("=========SqlSession2修改======== User:{}", JSON.toJSONString(user));
                User secondUser = userMapper2.selectById(user.getId());
                log.debug("=========SqlSession2查询======== User:{}", JSON.toJSONString(secondUser));
                User thirdUser = userMapper1.selectById(user.getId());
                log.debug("=========SqlSession1查询======== User:{}", JSON.toJSONString(thirdUser));
            } catch (Exception e) {
                log.error(e.getMessage());
            }
            return 1;
        }
    }
    
    

    数据库预先录入数据,然后测试修改用户名

  • 执行结果

    2020-05-26 14:52:21.869 DEBUG 3992 --- [nio-8080-exec-7] o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
    o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@1725242542 wrapping com.mysql.cj.jdbc.ConnectionImpl@7bd24a07] will not be managed by Spring
    c.e.demo.mapper.UserMapper.selectById    : ==>  Preparing: SELECT id,name,sex,phone,create_time,enable,version FROM user WHERE id=? AND enable=1 
    c.e.demo.mapper.UserMapper.selectById    : ==> Parameters: 12121212(Long)
    c.e.demo.mapper.UserMapper.selectById    : <==      Total: 1
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession1查询======== User:{"createTime":1590475920000,"enable":true,"id":12121212,"name":"wangwu","phone":"18111111111","sex":"MAN","version":1}
    o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
    o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@691008575 wrapping com.mysql.cj.jdbc.ConnectionImpl@4a631491] will not be managed by Spring
    c.e.demo.mapper.UserMapper.updateById    : ==>  Preparing: UPDATE user SET name=?, phone=?, create_time=? WHERE id=? AND enable=1 
    c.e.demo.mapper.UserMapper.updateById    : ==> Parameters: lisi(String), 18111111111(String), 2020-05-26 14:52:21.869(Timestamp), 12121212(Long)
    c.e.demo.mapper.UserMapper.updateById    : <==    Updates: 1
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession2修改======== User:{"createTime":1590475941869,"enable":false,"id":12121212,"name":"lisi","phone":"18111111111"}
    c.e.demo.mapper.UserMapper.selectById    : ==>  Preparing: SELECT id,name,sex,phone,create_time,enable,version FROM user WHERE id=? AND enable=1 
    c.e.demo.mapper.UserMapper.selectById    : ==> Parameters: 12121212(Long)
    c.e.demo.mapper.UserMapper.selectById    : <==      Total: 1
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession2查询======== User:{"createTime":1590475942000,"enable":true,"id":12121212,"name":"lisi","phone":"18111111111","sex":"MAN","version":1}
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession1查询======== User:{"createTime":1590475920000,"enable":true,"id":12121212,"name":"wangwu","phone":"18111111111","sex":"MAN","version":1}
    

    由执行结果可知,同一个Session中,相同查询会使用缓存,不同Session之间互不影响。当有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据
    在这里插入图片描述

  • 设置一级缓存级别为Statement

    修改SqlSessionConfig配置,新增如下

    MybatisConfiguration configuration = new MybatisConfiguration();
    configuration.setLocalCacheScope(LocalCacheScope.STATEMENT);
    sqlSessionFactoryBean.setConfiguration(configuration);
    

    执行结果:

    com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@a4a722
    com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
    o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@1748010355 wrapping com.mysql.cj.jdbc.ConnectionImpl@a4a722] will not be managed by Spring
    c.e.demo.mapper.UserMapper.selectById    : ==>  Preparing: SELECT id,name,sex,phone,create_time,enable,version FROM user WHERE id=? AND enable=1 
    c.e.demo.mapper.UserMapper.selectById    : ==> Parameters: 12121212(Long)
    com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Pool stats (total=1, active=1, idle=0, waiting=0)
    c.e.demo.mapper.UserMapper.selectById    : <==      Total: 1
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession1查询======== User:{"createTime":1590489590000,"enable":true,"id":12121212,"name":"关羽","phone":"12345678910","sex":"MAN","version":1}
    o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
    com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@6eac3382
    o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@1875164390 wrapping com.mysql.cj.jdbc.ConnectionImpl@6eac3382] will not be managed by Spring
    c.e.demo.mapper.UserMapper.updateById    : ==>  Preparing: UPDATE user SET name=?, phone=?, create_time=? WHERE id=? AND enable=1 
    c.e.demo.mapper.UserMapper.updateById    : ==> Parameters: 赵云(String), 12345678910(String), 2020-05-26 18:40:20.098(Timestamp), 12121212(Long)
    c.e.demo.mapper.UserMapper.updateById    : <==    Updates: 1
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession2修改======== User:{"createTime":1590489620098,"enable":false,"id":12121212,"name":"赵云","phone":"12345678910"}
    c.e.demo.mapper.UserMapper.selectById    : ==>  Preparing: SELECT id,name,sex,phone,create_time,enable,version FROM user WHERE id=? AND enable=1 
    c.e.demo.mapper.UserMapper.selectById    : ==> Parameters: 12121212(Long)
    c.e.demo.mapper.UserMapper.selectById    : <==      Total: 1
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession2查询======== User:{"createTime":1590489620000,"enable":true,"id":12121212,"name":"赵云","phone":"12345678910","sex":"MAN","version":1}
    c.e.demo.mapper.UserMapper.selectById    : ==>  Preparing: SELECT id,name,sex,phone,create_time,enable,version FROM user WHERE id=? AND enable=1 
    c.e.demo.mapper.UserMapper.selectById    : ==> Parameters: 12121212(Long)
    c.e.demo.mapper.UserMapper.selectById    : <==      Total: 1
    c.e.demo.service.impl.UserServiceImpl    : =========SqlSession1查询======== User:{"createTime":1590489620000,"enable":true,"id":12121212,"name":"赵云","phone":"12345678910","sex":"MAN","version":1}
    

    总结:Mybatis作为ORM框架,为了避免存在的脏读问题,建议修改一级缓存作用域为:Statement

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值