Mybatis开启二级缓存
1.在配置文件添加以下配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
cache-enabled: true # 打开二级缓存
2.在需要缓存的namespace中添加@CacheNamespace注解
执行Mappper中的方法流程
- 当调用mapper中的方法时,如
QueryWrapper<TblOneClickOrder> objectQueryWrapper = new QueryWrapper<>();
List<TblOneClickOrder> tblOneClickOrders = orderMapper.selectList(objectQueryWrapper);
时,会进入MybatisMapperProxy.invoke()方法中,代码如下
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
其中,cachedInvoker()方法是用于缓存invoke对象(没有则创建),代码如下
private MybatisMapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return (MybatisMapperProxy.MapperMethodInvoker)CollectionUtils.computeIfAbsent(this.methodCache, method, (m) -> {
if (m.isDefault()) {
try {
return privateLookupInMethod == null ? new MybatisMapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MybatisMapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
throw new RuntimeException(var4);
}
} else {
return new MybatisMapperProxy.PlainMethodInvoker(new MybatisMapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
}
});
} catch (RuntimeException var4) {
Throwable cause = var4.getCause();
throw (Throwable)(cause == null ? var4 : cause);
}
}
- 调用PlainMethodInvoker.invoke()方法,方法中调用了MabatisMapperMethod.execute()方法
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return this.mapperMethod.execute(sqlSession, args);
}
- MabatisMapperMethod.execute()会根据不同的执行类型来执行不同的操作,当前以selectList()方法进行了解
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else if (IPage.class.isAssignableFrom(this.method.getReturnType())) {
result = this.executeForIPage(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
5.调用当前类的executeForMany()方法
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
// 将条件查询封装成Map返回,param实际上是map
Object param = this.method.convertArgsToSqlCommandParam(args);
List result;
if (this.method.hasRowBounds()) {
// 处理分页类型
RowBounds rowBounds = this.method.extractRowBounds(args);
result = sqlSession.selectList(this.command.getName(), param, rowBounds);
} else {
// 非分页,进行深入这个方法
result = sqlSession.selectList(this.command.getName(), param);
}
if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
} else {
return result;
}
}
6.进入SqlSessionTemplate.selectList()方法,代码如下
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
// 接着会调用DefaultSqlSession.selectList()方法
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
7.DefaultSqlSession.selectList()方法
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
List var6;
try {
// statement是调用方法的全限定名,如com.at.order.core.mapper.OrderMapper.selectList
MappedStatement ms = this.configuration.getMappedStatement(statement);
var6 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler);
} catch (Exception var10) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var10, var10);
} finally {
ErrorContext.instance().reset();
}
return var6;
}
MybatisConfiguration.getMappedStatement()
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
// 这里面会做一些额外操作,如移除一些缓存引用
this.buildAllStatements();
}
// 这里返回构建好的mapperStatement,结构如下图
return (MappedStatement)this.mappedStatements.get(id);
}
8.调用CachingExcutor.query()方法,当开启二级缓存之后才会使用调用CachingExcutor
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 参数绑定,parameterObject也就是之前封装成map对象的param
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 由方法全限定名+分页参数(offset, limit) + sql等组成的cachekey,代码如下
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 当开启Namespace缓存时才会有
Cache cache = ms.getCache();
if (cache != null) {
// 存在缓存时的操作
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
// 调用的时BaseExcutor.query()方法
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 缓存没有命中,会将查询结果进行缓存
// tcm为TransactionalCacheManager对象,通过它来管理```
TransactionalCache对象,其中的entriesToAddOnCommit存储缓存
this.tcm.putObject(cache, key, list);
}
return list;
}
}
// 调用的时BaseExcutor.query()方法
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
Iterator var8 = parameterMappings.iterator();
while(var8.hasNext()) {
ParameterMapping parameterMapping = (ParameterMapping)var8.next();
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
Object value;
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 = this.configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (this.configuration.getEnvironment() != null) {
cacheKey.update(this.configuration.getEnvironment().getId());
}
return cacheKey;
}
}
- BaseExcutor.query()方法
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
// 从缓存中获取
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 执行sql
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
执行sql语句的方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
// 执行sql
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
// 一级缓存
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}