《深入理解mybatis原理》一级缓存
MyBatis提供了一级缓存、二级缓存 这两个缓存机制,能够很好地处理和维护缓存,以提高系统的性能。这里主要介绍一级缓存,深入源码,解析其实现原理。
一级缓存介绍及用处
  每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。
  在一次数据库会话中,可能会反复执行完全相同的sql查询,在大数据量的情况下,这会造成极大的资源浪费。为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来。当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。
  如下图所示,mybatis在一次会话(即一个SqlSession对象)中,会创建一个本地存储。对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否存在,如果存在则直接返回返回;否则则去查询数据库并将结果存入。
Myabtis中的一级缓存结构
  之前看到Mybatis利用动态代理使用MapperMethod.execute来执行所有的数据库操作。实际上继续往下阅读可以发现最底层是调用了SqlSession中Executor对象的相关方法。
  当创建了一个SqlSession对象时,会在内部创建一个Executor执行器对象,缓存信息Cache就存储在这个对象中。SqlSession、Executor、Cache之间的关系如下列类图所示:

  如上述的类图所示,Executor接口的实现类BaseExecutor中拥有一个Cache接口的实现类PerpetualCache,来实现对缓存的维护。
下面是PeroetualCache的源码:
1 | public class PerpetualCache implements Cache { |
可以发现源码十分简单,就是用一个Map来实现存储,key值为一次查询的唯一标识,value则为一次查询的结果。
一级缓存生命周期
MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。- 如果
SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用; - 如果
SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用; SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;
一级缓存唯一标识(CacheKey)
  Mybatis会对同一次会话中的相同查询进行缓存,那么Mybatis是根据什么条件判断两次查询相同呢。
  以下为判断部分源码:
1 |
|
可以看到createCacheKey方法有四个参数,也就是对应的判断条件:
- 传入的
statementId(即接口名+方法名) - 查询时要求的结果集中的结果范围
- 这次查询所产生的最终要传递给JDBC
java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql()) - 传递给
java.sql.Statement要设置的参数值
MyBatis认为的完全相同的查询,不是指使用sqlSession查询时传递给SqlSession的所有参数值完完全全相同,你只要保证statementId,rowBounds,最后生成的SQL语句,以及这个SQL语句所需要的参数完全一致就可以了。