概要:默认情况下,MyBatis只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。这也就是大家常说的MyBatis一级缓存,一级缓存的作用域是SqlSession。
-
作用域
取值有两个:SqlSession和Statement文档: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