1.一级缓存
在mybatis中,一级缓存默认是开启的,并且无法关闭。一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,mybatis会把执行的方法和参数生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已存在该键值时,则会返回缓存中的对象。
测试:
@Test public void testQueryUserById() { System.out.println(this.userMapper.queryUserById("1")); System.out.println(this.userMapper.queryUserById("1")); }
执行日志:
2018-07-01 17:08:50,156 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection 2018-07-01 17:08:50,421 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057. 2018-07-01 17:08:50,423 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 2018-07-01 17:08:50,476 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String) 2018-07-01 17:08:50,509 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1 User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'} User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
使用:sqlSession.clearCache();可以强制清除缓存
测试:
@Test public void testQueryUserById() { System.out.println(this.userMapper.queryUserById("1")); sqlSession.clearCache(); System.out.println(this.userMapper.queryUserById("1")); }
日志:
2018-07-01 17:10:51,065 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection 2018-07-01 17:10:51,359 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057. 2018-07-01 17:10:51,360 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 2018-07-01 17:10:51,408 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String) 2018-07-01 17:10:51,437 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1 User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'} 2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String) 2018-07-01 17:10:51,440 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1 User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
执行update、insert、delete的时候,会清空缓存
测试:
@Test public void testQueryUserById() { System.out.println(this.userMapper.queryUserById("1")); //sqlSession.clearCache(); User user=new User(); user.setName("美女"); user.setId("1"); userMapper.updateUser(user); System.out.println(this.userMapper.queryUserById("1")); }
日志:
2018-07-01 17:18:15,128 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection 2018-07-01 17:18:15,399 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057. 2018-07-01 17:18:15,401 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 2018-07-01 17:18:15,466 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String) 2018-07-01 17:18:15,492 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1 User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'} 2018-07-01 17:18:15,527 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Preparing: UPDATE tb_user set name = ?, updated = now() WHERE (id = ?); 2018-07-01 17:18:15,529 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Parameters: 美女(String), 1(String) 2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] <== Updates: 1 2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 2018-07-01 17:18:15,533 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String) 2018-07-01 17:18:15,538 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1 User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
一级缓存在多线程场景下可能会导致脏读
测试代码:
public class MybatisTest2 { public static void main(String[] args) throws Exception { // 指定全局配置文件 String resource = "mybatis-config.xml"; // 读取配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); // 构建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.queryUserById(1); System.out.println(user); Thread.sleep(1000*60);//此时可以手动修改数据库中数据 User user2 = userMapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); } finally { sqlSession.close(); } } }
测试结果:两次返回的对象是同一个,第二次返回的是缓存中数据。
2020-04-21 11:11:18,921 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1738710757. 2020-04-21 11:11:18,921 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67a29ee5] 2020-04-21 11:11:18,928 [main] [com.mybatis.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 2020-04-21 11:11:19,041 [main] [com.mybatis.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(Integer) 2020-04-21 11:11:19,158 [main] [com.mybatis.UserMapper.queryUserById]-[DEBUG] <== Total: 1 User(id=1, userName=zpc, password=123456, name=鹏程, age=33, sex=1, birthday=Sun Sep 02 00:00:00 CDT 1990, created=2020-04-19 11:14:40.0, updated=2020-04-19 11:14:40.0) User(id=1, userName=zpc, password=123456, name=鹏程, age=33, sex=1, birthday=Sun Sep 02 00:00:00 CDT 1990, created=2020-04-19 11:14:40.0, updated=2020-04-19 11:14:40.0) true 2020-04-21 11:12:19,161 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67a29ee5]
此时数据库中真实数据:
解决方法:
根据数据安全性划分,像评论等对用业务影响不大的数据,保持使用一级缓存,提高查询效率。
像价格等对业务影响很大的数据,每次查询时清空缓存或者每次查询都新建个sqlSession对象。
清空缓存:
方式一:代码里手动调用下面方法
sqlSession.clearCache();
方式二:mapper.xml配置文件里对应查询sql增加 flushCache="true" 配置
<select id="queryUserById" flushCache="true" resultType="com.mybatis.User"> select * from tb_user where id = #{id} </select>
2.二级缓存
mybatis 的二级缓存的作用域是一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。
开启二级缓存:
<mapper namespace="com.zpc.mybatis.dao.UserMapper"> <cache/> </mapper>
测试:
@Test public void testCache() { System.out.println(this.userMapper.queryUserById("1")); sqlSession.close(); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); System.out.println(mapper.queryUserById("1")); }
开启二级缓存,必须序列化:
public class User implements Serializable{ private static final long serialVersionUID = -3330851033429007657L;
日志:
2018-07-01 17:23:39,335 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection 2018-07-01 17:23:39,664 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 2092769598. 2018-07-01 17:23:39,665 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 2018-07-01 17:23:39,712 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String) 2018-07-01 17:23:39,734 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1 User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'} 2018-07-01 17:23:39,743 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cbd213e] 2018-07-01 17:23:39,744 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 2092769598 to pool. 2018-07-01 17:23:39,746 [main] [com.zpc.mybatis.dao.UserMapper]-[DEBUG] Cache Hit Ratio [com.zpc.mybatis.dao.UserMapper]: 0.5 User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
关闭二级缓存:
不开启,或者在全局的mybatis-config.xml 中去关闭二级缓存
<settings> <!--开启驼峰匹配--> <setting name="mapUnderscoreToCamelCase" value="true"/> <!--开启二级缓存,全局总开关,这里关闭,mapper中开启了也没用--> <setting name="cacheEnabled" value="false"/> </settings>