概要:默認情況下,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