Mybatis cache -- first level cache and source code analysis

IsDxh 2020-11-11 10:39:54
mybatis cache level cache source


What is caching ?

Caching is data stored in memory , And memory reads are very fast , Usually we put Update changes are less frequent And Frequent queries The data of , After the first query from the database , Stored in the cache , In this way, you can avoid multiple interactions with the database , So as to improve the response speed .

mybatis It also provides cache support , It is divided into :

  • First level cache
  • Second level cache

image-20201110221457670

  1. First level cache :
    Every sqlSeesion Objects have a first level cache , We need to construct sqlSeesion object , There is one in the object HashMap For storing cached data . Different sqlSession Cache data area between (HashMap) It doesn't affect each other .
  2. Second level cache :
    The L2 cache is mapper Level ( Also known as namespace Level ) The cache of , Multiple sqlSession To operate the same Mapper Of sql sentence , Multiple SqlSession Second level cache can be shared , Second level cache is cross sqlSession Of .

First level cache

First of all, let's open the first level cache , The first level cache is Default on Of , So it's very convenient for us to experience the first level cache .

Test one 、

Prepare a table , There are two fields id and username
image-20201110232032148

In the test class :

public class TestCache {
private SqlSession sqlSession;
private UserMapper mapper;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = build.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testFirst(){
// First query ———— First of all, go to the first level cache to query
User user1 = mapper.findById(1);
System.out.println("======"+user1);
// Second query
User user2 = mapper.findById(1);
System.out.println("======"+user2);
System.out.println(user1==user2);
}
}

We use the same sqlSession Respectively according to the id To query users ,id All for 1, Then compare their address values . Take a look at the results :

23:16:25,818 DEBUG findById:159 - ==> Preparing: select * from user where id=?
23:16:25,862 DEBUG findById:159 - ==> Parameters: 1(Integer)
23:16:25,894 DEBUG findById:159 - <== Total: 1
======User{id=1, username='lucy'}
======User{id=1, username='lucy'}
true

We found that there was only one print SQL, At the same time, their address values are the same .

Explain the first query , Not in cache , And then query from the database —— perform SQL, And then it's stored in the cache , The second query found that there was , So just take it out of the cache , No more execution SQL 了 .
image-20201110233551326

We just mentioned , The data structure of L1 cache is a hashmap, That is to say, there are key Yes value.
value That's what we found out ,key It's made up of multiple values :

  • statementid :namespace.id form
  • params: The parameters passed in when querying
  • boundsql:mybatis The underlying object , It encapsulates what we're going to execute sql
  • rowbounds: Paging object
  • ... There are also some will explain in the source code analysis

Test two 、

Let's modify it now , After we checked the first result , Change the value of the database , And then make a second query , Let's take a look at the query results .id=1 Of username by lucy
image-20201110232032148

 @Test
public void testFirst(){
// First query
User user1 = mapper.findById(1);
System.out.println("======"+user1);
// modify id by 1 Of username
User updateUser = new User();
updateUser.setId(1);
updateUser.setUsername(" Lee thought ");
mapper.updateUser(updateUser);
// Commit transactions manually
sqlSession.commit();
// Second query
User user2 = mapper.findById(1);
System.out.println("======"+user2);
System.out.println(user1==user2);
}

image-20201110235221440

Make a breakpoint where the transaction is committed , You can see that two of them are implemented sql, One is to inquire id by 1, One is modification id by 1 Of username

final result :

23:50:15,933 DEBUG findById:159 - ==> Preparing: select * from user where id=?
23:50:15,976 DEBUG findById:159 - ==> Parameters: 1(Integer)
23:50:16,002 DEBUG findById:159 - <== Total: 1
======User{id=1, username='lucy', roleList=null, orderList=null}
23:50:16,003 DEBUG updateUser:159 - ==> Preparing: update user set username=? where id =?
23:50:16,005 DEBUG updateUser:159 - ==> Parameters: Lee thought (String), 1(Integer)
23:50:16,016 DEBUG updateUser:159 - <== Updates: 1
23:53:18,316 DEBUG JdbcTransaction:70 - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@421e361]
23:53:22,306 DEBUG findById:159 - ==> Preparing: select * from user where id=?
23:53:22,306 DEBUG findById:159 - ==> Parameters: 1(Integer)
23:53:22,307 DEBUG findById:159 - <== Total: 1
======User{id=1, username=' Lee thought ', roleList=null, orderList=null}

We see , And finally printed 3 strip sql, The second query after modification is also printed .

Indicates that the corresponding... Cannot be found in the cache during the second query key 了 . In the modification operation , Refresh cache

We can also pass sqlSession.clearCache(); Refresh the first level cache manually

summary :

  • First level data structure HashMap
  • Different SqlSession The first level of cache does not affect each other
  • First level cache key It's made up of multiple values ,value It's the query result
  • Adding, deleting and modifying will refresh the first level cache
  • adopt sqlSession.clearCache() Refresh the first level cache manually

First level cache source code analysis :

Before we analyze the L1 cache Read the code with some questions

  1. What is the first level cache ? It's really what the above says HashMap Do you ?

  2. When the L1 cache is created ?

  3. What is the workflow of L1 cache ?

1. What is the first level cache ?

I said before Different SqlSession The first level of cache does not affect each other , So I went from SqlSession Start with this class
image-20201111005238379

You can see ,org.apache.ibatis.session.SqlSession There is a cache related method in ——clearCache() How to refresh the cache , Click in , Find its implementation class DefaultSqlSession

 @Override
public void clearCache() {
executor.clearLocalCache();
}

Click in again executor.clearLocalCache(), Click in again and find the implementation class BaseExecutor,

 @Override
public void clearLocalCache() {
if (!closed) {
localCache.clear();
localOutputParameterCache.clear();
}
}

Get into localCache.clear() Method . Into org.apache.ibatis.cache.impl.PerpetualCache Class

package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
/**
* @author Clinton Begin
*/
public class PerpetualCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
// Omit the part ...
@Override
public void clear() {
cache.clear();
}
// Omit the part ...
}

We see that PerpetualCache There is an attribute in the class private Map<Object, Object> cache = new HashMap<Object, Object>(), Obviously it's a HashMap, What we call .clear() Method , It's actually called Map Of clear Method
image-20201111010052591

Come to the conclusion :

The data structure of L1 cache is really HashMap
image-20201111011712449

2. When the L1 cache is created ?

We enter org.apache.ibatis.executor.Executor in
See a way CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) , To see the name and think means to create CacheKey Methods
Find its implementation class and method org.apache.ibatis.executor.BaseExecuto.createCacheKey

image-20201111012213242

Let's analyze the creation of CacheKey This code of :

public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// initialization CacheKey
CacheKey cacheKey = new CacheKey();
// Deposit in statementId
cacheKey.update(ms.getId());
// Save the required pages separately Offset and Limit
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
// Take from BoundSql The encapsulation of sql Take out and deposit in cacheKey In the object
cacheKey.update(boundSql.getSql());
// Here's the package parameter
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
// from configuration In the object ( That is, the object stored after loading the configuration file ) hold EnvironmentId Deposit in
/**
* <environments default="development">
* <environment id="development"> // This is this. id
* <!-- The present business is left to JDBC Conduct management -->
* <transactionManager type="JDBC"></transactionManager>
* <!-- The current use of mybatis Provided connection pool -->
* <dataSource type="POOLED">
* <property name="driver" value="${jdbc.driver}"/>
* <property name="url" value="${jdbc.url}"/>
* <property name="username" value="${jdbc.username}"/>
* <property name="password" value="${jdbc.password}"/>
* </dataSource>
* </environment>
* </environments>
*/
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
// return
return cacheKey;
}

Let's go in cacheKey.update() Take a look at

/**
* @author Clinton Begin
*/
public class CacheKey implements Cloneable, Serializable {
private static final long serialVersionUID = 1146682552656046210L;
public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();
private static final int DEFAULT_MULTIPLYER = 37;
private static final int DEFAULT_HASHCODE = 17;
private final int multiplier;
private int hashcode;
private long checksum;
private int count;
// Where the value is deposited
private transient List<Object> updateList;
// Omission method ......
// Omission method ......
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
count++;
checksum += baseHashCode;
baseHashCode *= count;
hashcode = multiplier * hashcode + baseHashCode;
// A value is passed in to see list in
updateList.add(object);
}
// Omission method ......
}

We know that the data is in CacheKey How to store in the object . Let's go back to createCacheKey() Method .
image-20201111013322287

Ctrl+ Left mouse button Click on the method name , Query where this method is called

We enter BaseExecutor, You can see a query() Method :
image-20201111013443089

Here we can see clearly , In execution query() Before the method ,CacheKey Methods are created

3. The execution flow of L1 cache

We can see , establish CacheKey Then called. query() Method , Let's click in again :

image-20201111014034187

In execution SQL How to find in the first level cache Key, Then it will be executed sql, Let's take a look at execution sql What will be done before and after , Get into list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
image-20201111014639468

Look at the :

 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//1. hold key Deposited in the cache ,value Put a place holder
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//2. Interacting with the database
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//3. If the first 2 What's wrong with it , The first 1 Step deposited key Delete
localCache.removeObject(key);
}
//4. Cache the results
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}

thus , Our thinking is very clear .

Conclusion :

In execution sql front , According to CacheKey Is there any query in the cache , If there is , Just deal with the parameters in the cache , without , Is executed sql, perform sql Then put the results in the cache .

First level cache source code analysis conclusion :

  1. The data structure of L1 cache is a HashMap<Object,Object>, its value It's the query result , its key yes CacheKey,CacheKey There is one of them. list attribute ,statementId,params,rowbounds,sql All the parameters are stored in this list in
  2. The first level cache is called query() Method is created before . And passed in to query() In the method
  3. According to CacheKey Is there any query in the cache , If there is , Just deal with the parameters in the cache , without , Is executed sql, perform sql Then put the results in the cache .
版权声明
本文为[IsDxh]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云