Mybatis 执行过程及缓存原理

111 阅读3分钟

Mybatis开启二级缓存

1.在配置文件添加以下配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    cache-enabled: true # 打开二级缓存
2.在需要缓存的namespace中添加@CacheNamespace注解

执行Mappper中的方法流程

  1. 当调用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);
    }
}
  1. 调用PlainMethodInvoker.invoke()方法,方法中调用了MabatisMapperMethod.execute()方法
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
   return this.mapperMethod.execute(sqlSession, args);
}
  1. 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);
}

屏幕截图 2022-10-05 232611.png

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;
    }
}
  1. 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;
}