mybatis中的缓存
-
什么是缓存?
缓存就是存在内存中的临时数据
-
为什么使用缓存?
减少和数据库中的交互次数,提高执行效率
-
什么样的数据可以使用缓存,什么样的数据不能使用缓存?
适用:
经常查询并且不经常改变的数据
数据的正确与否对最终结果影响不大的时候
不适用:
经常改变的数据
数据的正确与否对最终结果影响很大的
例如:商品的库存,银行的汇率
mybatis中的一级缓存与二级缓存
-
一级缓存:
指的是mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有这个数据,有的话会直接拿出来用,而不再去数据库中查询。
当SqlSession对象消失时,mybatis一级缓存也就消失了。
@Test public void firstLevelCatch(){ System.out.println("---------------------------------------------------------------------------"); User user = userDao.selectOne(1); System.out.println(user); System.out.println("---------------------------------------------------------------------------"); User user1 = userDao.selectOne(1); System.out.println(user1); System.out.println("---------------------------------------------------------------------------"); }
当执行单表的按ID查询方法时,执行两次该方法,看log4j的打印信息。在第一次的时候是从数据库里查找出来信息,第二次时就是从缓存中将上一次的消息再次拿出来
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. --------------------------------------------------------------------------- Opening JDBC Connection Created connection 1073533248. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ffcd140] ==> Preparing: select * from user where id = ?; ==> Parameters: 1(Integer) <== Columns: id, name, age, birthday <== Row: 1, 1, 1, 2020-12-03 14:17:30.0 <== Total: 1 User{id=1, name='1', age=1, birthday=Thu Dec 03 14:17:30 CST 2020} --------------------------------------------------------------------------- User{id=1, name='1', age=1, birthday=Thu Dec 03 14:17:30 CST 2020} --------------------------------------------------------------------------- Process finished with exit code 0
这是控制台的打印信息,我在两次查询中间用了同样的短线进行划分,可以很明显的看出,第一次在数据库中进行了查找,第二次没有。
那如何直接清理掉SqlSession中的缓存呢?有两种方式:
- 就是关闭SqlSession,然后再重新创建一个SqlSession,这样,以前的SqlSession就会消失,缓存自然也会消失
- 调用方法,调用SqlSession的clearCache()方法来清空缓存。
第一种方法我们不多做赘述,来看下第二种方法的效果
@Test public void firstLevelCatch(){ System.out.println("---------------------------------------------------------------------------"); User user = userDao.selectOne(1); System.out.println(user); session.clearCache(); System.out.println("---------------------------------------------------------------------------"); User user1 = userDao.selectOne(1); System.out.println(user1); System.out.println("---------------------------------------------------------------------------"); }
这是执行方法,可以看出两次查询中间添加了清理缓存的方法
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. --------------------------------------------------------------------------- Opening JDBC Connection Created connection 1073533248. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ffcd140] ==> Preparing: select * from user where id = ?; ==> Parameters: 1(Integer) <== Columns: id, name, age, birthday <== Row: 1, 1, 1, 2020-12-03 14:17:30.0 <== Total: 1 User{id=1, name='1', age=1, birthday=Thu Dec 03 14:17:30 CST 2020} --------------------------------------------------------------------------- ==> Preparing: select * from user where id = ?; ==> Parameters: 1(Integer) <== Columns: id, name, age, birthday <== Row: 1, 1, 1, 2020-12-03 14:17:30.0 <== Total: 1 User{id=1, name='1', age=1, birthday=Thu Dec 03 14:17:30 CST 2020} --------------------------------------------------------------------------- Process finished with exit code 0
这是控制台的打印效果,可以看出,进行了两次数据库的查询操作。
注:一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。(请自行验证)
-
二级缓存:
指的是mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存
二级缓存的使用步骤:
-
让mybatis框架支持二级缓存(在mybatis配置文件中配置)
<setting name="cacheEnabled" value="true"/>
也可以不配置,默认为true
-
让当前的映射文件支持二级缓存(到接口配置文件中配置)
在接口配置文件的mapper标签下添加
<cache></cache>
-
让当前的操作支持二级缓存(select标签中配置)
然后在要实现二级缓存的语句标签上加入useCache=“true”
<select id="selectOne" parameterType="com.lut.domain.User" useCache="true" resultType="com.lut.domain.User"> select * from user where id = #{id}; </select>
当完成以上配置,在测试类中创建两个SqlSession,调用同一个查询方法,查询同一个值,第二次查询是从缓存中取,如果不实现二级缓存,是分别从数据库查询
@Test public void firstLevelCatch(){ System.out.println("---------------------------------------------------------------------------"); SqlSession session = factory.openSession(); IUserDao userDao = session.getMapper(IUserDao.class); User user = userDao.selectOne(1); System.out.println(user); session.close(); System.out.println("---------------------------------------------------------------------------"); SqlSession session1 = factory.openSession(); IUserDao userDao = session1.getMapper(IUserDao.class); User user1 = userDao.selectOne(1); System.out.println(user1); session1.close(); System.out.println("---------------------------------------------------------------------------"); }
-
注:一级缓存存储的是对象,所以两次查询对象相等
二级缓存存储的是数据,所以两次查询对象不相等