Mybatis
Article Directory
- Mybatis
-
- mybatis loading process
- Mybatis first level cache
-
- Level 1 cache
-
- Introduction
- Source code analysis
-
- 1. Enter the implementation of the () method of clearCache DefaultSqlSession
- 2. Enter the Executor interface
- 3. Enter the implementation of the clearLocalCache() method in Executor
- 4. Enter the localCache.clear() method:
- 5.cache.clear(), what is the cache object?
- 1. The method in Executor, the method for creating a cache is the createCacheKey() method
- Secondary cache
mybatis loading process
-
The Config.xml global configuration file is parsed and placed in the configuration object. The annotation configuration and SQL configuration files in Java are encapsulated in the statement object and stored in memory, which is also maintained by the configuration object
-
Mapping file loading: the mappedStatement object parses the sql, and each sql object corresponds to a statementID, and stores the parsed sql in the map, the key is the statementId, and the value is the parsed sql, which is stored in the sqlsource
-
sqlSource stores sql information and also provides sql parsing functions
dynamicsqlSource:${} parse this kind of dynamic sql
RawsqlSource:#{}Analyze this kind of dynamic sql
StaticsqlSource: After parsing the above two kinds of sql, store it in sqlSource, through this sqlSource can get BoundSql
combined with staticSqlSource source code, it will be easier to understand
public class StaticSqlSource implements SqlSource {
private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Configuration configuration;
public StaticSqlSource(Configuration configuration, String sql) {
this(configuration, sql, null);
}
public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.configuration = configuration;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
}
-
BoundSql:
String sql; parsed sql
List ParameterMappings; corresponding to the incoming parameters
sqlNode: tree structure
sqlSource stores sql information, which is a collection of sqlNode
Mybatis first level cache
Mybatis has a first-level cache and a second-level cache
Level 1 cache
Introduction
The first level cache is based on sqlSession
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
DemoMapper demoMapper = sqlSession.getMapper(DemoMapper.class);
// 第一次查询
Demo demo1 = demoMapper.findById(1);
// 第二次查询
Demo demo2 = demoMapper.findById(1);
The above code uses the same demoMapper for two consecutive queries. The first query will indeed connect and query the database, and the second time, it will go to the first level cache of mybatis to fetch it. That is, after the first query, the query result will be stored in the first-level cache, and then the query will be searched in the first-level cache again after the query, and it will be returned if it matches, and it will be queried in the database if it does not match.
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
DemoMapper demoMapper = sqlSession.getMapper(DemoMapper.class);
// 第一次查询
Demo demo1 = demoMapper.findById(1);
//更新
Demo demo = new User();
demo.setId(1);
demo.setName("test");
demoMapper.updateDemo(demo);
// 第二次查询
Demo demo2 = demoMapper.findById(1);
In the above code, after the first query, the data is updated again, and then the second query is performed.
Process :
- The first query connects to the database and stores the query results in the first-level cache.
- When the data is updated, the update operation will clear the first-level cache, so the corresponding cache in the first-level cache is emptied at this time.
- In the second query, it will not be obtained in the first-level cache, so go to the database to query.
Source code analysis
We find the SqlSesion interface. The one that seems to have an effect on the first level cache is a clearCache() method
1. Enter the implementation of the () method of clearCache DefaultSqlSession
The source code is as follows: For the convenience of viewing, I will not take a screenshot and paste the source code directly
public void clearCache() {
this.executor.clearLocalCache();
}
2. Enter the Executor interface
3. Enter the implementation of the clearLocalCache() method in Executor
The source code implemented in the BaseExecutor class is as follows
public void clearLocalCache() {
if (!this.closed) {
this.localCache.clear();
this.localOutputParameterCache.clear();
}
}
4. Enter the localCache.clear() method:
The source code of the PerpetualCache class is as follows
public void clear() {
this.cache.clear();
}
5.cache.clear(), what is the cache object?
Looking at the source code below, the original object that executes clear is just a simple map object. From here we can know that the first level cache of mybatis is stored using Map.
-------------------------------------------------- --------------------Dividing line---------------------------- ------------------------------------------------
The source code introduced above is the clearing of the first-level cache. What about creation?
Let's review the process of the above source code
SqlSession–DefaultSqlSession–Executor–BaseExecutor–PerpetualCache
What is Executor? Executor, we know that the executor is used to execute the SQL request. Since the emptying of the cache is executed in the exctor, the operation of storing the result and storing the result together with the cache after executing the SQL request must also be executed here.
1. The method in Executor, the method for creating a cache is the createCacheKey() method
2. Go to the implementation class BaseExecutor to see the specific implementation
The source code is as follows:
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
Note that this line of code CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
The source code of createCacheKey() method here: The content is directly written to the comment of the following code
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// boundSQL的ID,应该就是那个statementID,类似路径包名+类名+sql这样
cacheKey.update(ms.getId());
// 就是SQL中使用的offset,控制查询范围的那种
cacheKey.update(rowBounds.getOffset());
// SQL中有没有limit限制查询数量
cacheKey.update(rowBounds.getLimit());
// 这里就是具体的SQL语句了
cacheKey.update(boundSql.getSql());
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);
}
// 这里value是什么,可以去回顾下加载过程,ParameterMapping存储的是SQL中的参数,所以这里就是存储的参数信息
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
To sum up: the first level cache is created here. What is stored in it has been remarked in the comment above, and I will list it below
CacheKey cacheKey = new CacheKey();
// boundSQL的ID,应该就是那个statementID,类似路径包名+类名+sql这样
cacheKey.update(ms.getId());
// 就是SQL中使用的offset,控制查询范围的那种
cacheKey.update(rowBounds.getOffset());
// SQL中有没有limit限制查询数量
cacheKey.update(rowBounds.getLimit());
// 这里就是具体的SQL语句了
cacheKey.update(boundSql.getSql());
// 这里value是什么,可以去回顾下加载过程,ParameterMapping存储的是SQL中的参数,所以这里就是存储的参数信息
cacheKey.update(value);
Secondary cache
Introduction
The secondary cache also stores query results and the corresponding SQL. After the query is completed, it is placed in the cache, and the query can be retrieved from it again.
So what is the difference between it and the first level cache
The first level cache has been introduced above and is based on SQLSession, that is, the same sqlSession.
The second-level cache is based on the nameSpace of the mapper, which is the second-level cache of a mapper (or multiple mappers with the same nameSpace). There can be multiple sqlSessions.
And the second level cache needs to be manually turned on
use
Add the following code in the global configuration file
Then add the following code to the specific mapper you need to use
The implementation class used by mybatis by default is the PerpetualCache cache class traced from the above source code, which implements the cache interface. So here we can also define the implementation class of the second-level cache and implement the cache interface.
Distributed secondary cache
How does mybatis implement distributed caching? If you operate according to the above cache, it is only a single cache.
Distributed cache: multiple servers can access the operation cache
Here we can use Redis combined with mybatis to implement distributed caching
The method of use is as follows:
1. Pom file increase dependency
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
2. Specify the cache implementation class to be used in the mapper used
<cache type="org.mybatis.caches.redis.RedisCache"></cache>
3. Configure redis information, etc.
Source code analysis
Let's take a look at the secondary cache implemented by redisCache
1. Construction method
From the code here, we can see that the secondary cache implemented by Redis uses jedis to operate.
public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(),
redisConfig.getDatabase(), redisConfig.getClientName());
}
2. Put get method. From here, what is the storage structure of the secondary cache?
From the hset command, it is easy to know that the hash structure is used to store cached data
@Override
public void putObject(final Object key, final Object value) {
execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
jedis.hset(id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));
return null;
}
});
}
@Override
public Object getObject(final Object key) {
return execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
return SerializeUtil.unserialize(jedis.hget(id.toString().getBytes(), key.toString().getBytes()));
}
});
}