Mybatis 框架源码解读(详细流程图+时序图)

看源码都要带着问题去看,比如

  1. UserMapper.java只有接口而没有实现类,那么是如何执行的呢?
  2. mybatis中一级缓存是如何进行缓存与维护的?
  3. 底层是如何执行query查询的
  4. 查询后的结果是如何处理的,为什么可以将结果集自动映射到对象中去?

让我们带着上面的问题来到mybatis框架底层一探究竟吧

四大组件

开始之前我们先了解下几个重要的组件它们的作用

我们使用的时候,都是从SqlSession中的对象开始,它是属于接口层
Mapper执行的过程是通过ExecutorStatementHandlerParameterHandlerResultHandler来完成数据库操作和结果返回的:

  • Executor

    代表执行器,由它来调度StatementHandler、ParameterHandler、ResultHandler等来执行对应的SQL。

  • StatementHandler

    作用是使用数据库的Statement(PreparedStatement)执行操作,起到承上启下的作用。

  • ParameterHandler

    用于SQL对参数的处理。

  • ResultHandler

    进行最后数据集(ResultSet)的封装返回处理的。

查询User记录的简单例子


    public static void main(String[] args) {
        SqlSession sqlSession = null;
        try {
        	// 获取到sqlSessiion
            sqlSession = SqlSessionFactoryUtil.openSqlSession();
            // 获取到Mapper
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 查询记录
            User user = userMapper.getUserById(2L);
            System.out.println("姓名:" + user.getName() + ",年龄:" + user.getAge());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

从查询一条用户记录开始为起点,进行源码分析

六大阶段

  • 阶段1:获得Mapper动态代理阶段
  • 阶段2:获得MapperMethod对象
  • 阶段3:根据SQL指令跳转执行语句
  • 阶段4:查询前的缓存处理
  • 阶段5:执行DB查询操作
  • 阶段6:针对ResultSet结果集转换为POJO

第一阶段:获取Mapper动态代理阶段

Mybatis 框架源码解读(详细流程图+时序图)
我们自定义的Mapper接口想要发挥功能,必须有具体的实现类,在MyBatis中是通过为Mapper每个接口提供一个动态代理类来实现的。整个过程主要有四个类:MapperRegistry、MapperProxyFactory、MapperProxy、MapperMethod

  • MapperRegistry
    是Mapper接口及其对应的代理对象工厂的注册中心
  • MapperProxyFactory
    是MapperProxy的工厂类,主要方法就是包装了Java动态代理的Proxy.newProxyInstance()方法。
  • MapperProxy
    是一个动态代理类,它实现了InvocationHandler接口。对于代理对象的调用都会被代理到InvocationHandler#invoke方法上。
  • MapperMethod
    包含了具体增删改查方法的实现逻辑。

第一阶段关键代码:

sqlSession.getMapper(UserMapper.class);

调用的是DefaultSqlSession中的getMapper()方法

public class DefaultSqlSession implements SqlSession {
	...
    // eg1: type=UserMapper.class
    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.<T>getMapper(type, this);
    }
    ...
}

然后进入Configuration,这个类是mybatis配置信息类,将mybatis的xml配置文件和映射mapper的xml配置文件,读入到该类中进行使用

public class Configuration {
    // eg1: type=UserMapper.class sqlSession=DefaultSqlSession实例
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }
}

MapperRegistry是Mapper接口及其对应的代理对象工厂的注册中心

public class MapperRegistry {
    // eg1: type=UserMapper.class
    @SuppressWarnings("unchecked")
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        /**
         * 加载mybatis-config.xml配置的<mapper>配置,根据指定type,查找对应的MapperProxyFactory对象
         **/
        // eg1: 获得UserMapper的mapperProxyFactory
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        /**
         * 如果没配置<mapper>,则找不到对应的MapperProxyFactory,抛出BindingException异常
         */
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            /** 使用该工厂类生成MapperProxy的代理对象*/
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }
}

关键方法在:mapperProxyFactory.newInstance(sqlSession);

public class MapperProxyFactory<T> {
    /**
     * 通过动态代理,创建mapperInterface的代理类对象
     */
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {

        /**
         * 创建MapperProxy对象,每次调用都会创建新的MapperProxy对象,MapperProxy implements InvocationHandler
         */
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
}

接下来就是一直返回到最初的调用为UserMapper对象

总的来说就是通过预先扫描的mapper接口,然后对此接口进行JDK动态代理,然后通过代理实现sql的执行(就是invoke())

第二阶段:获取MapperMethod对象

执行如下代码的源码过程分析:

User user = userMapper.getUserById(2L);

Mybatis 框架源码解读(详细流程图+时序图)

    /**
     * 对代理类的所有方法的执行,都会进入到invoke方法中
     */
    // eg1: UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //      User user = userMapper.getUserById(2L); args = {2L}
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            /** 如果被代理的方法是Object类的方法,如toString()、clone(),则不进行代理 */
            // eg1: method.getDeclaringClass()==interface mapper.UserMapper  由于被代理的方法是UserMapper的getUserById方法,而不是Object的方法,所以返回false
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            /** 如果是接口中的default方法,则调用default方法 */
            else if (isDefaultMethod(method)) { // eg1: 不是default方法,返回false
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
        /** 初始化一个MapperMethod并放入缓存中 或者 从缓存中取出之前的MapperMethod */
        final MapperMethod mapperMethod = cachedMapperMethod(method);

        // eg1: sqlSession = DefaultSqlSession@1953  args = {2L}
        /** 调用MapperMethod.execute()方法执行SQL语句 */
        return mapperMethod.execute(sqlSession, args);
    }

然后我们来看下如何获取MapperMethod对象:

    private MapperMethod cachedMapperMethod(Method method) {
        /**
         * 在缓存中查找MapperMethod,若没有,则创建MapperMethod对象,并添加到methodCache集合中缓存
         */
        // eg1: 因为methodCache为空,所以mapperMethod等于null
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            // eg1: 构建mapperMethod对象,并维护到缓存methodCache中
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            // eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }

主要方法在new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());,进入这个类看看构造函数:

    // 记录了SQL语句的名称和类型
    private final SqlCommand command;

    // Mapper接口中对应方法的相关信息
    private final MethodSignature method;

    // eg1: mapperInterface = interface mapper.UserMapper
    //      method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)
    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, mapperInterface, method);
    }

SqlCommand:主要维护两个属性

        /** MappedStatement的唯一标识id */
        private final String name;

        /** sql的命令类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH; */
        private final SqlCommandType type;

MethodSignature:主要用于维护方法签名,比如方法的返回值类型、入参名称等等。具体参数如下:

        private final boolean returnsMany;                  // 判断返回类型是集合或者数组吗
        private final boolean returnsMap;                   // 判断返回类型是Map类型吗
        private final boolean returnsVoid;                  // 判断返回类型是集void吗
        private final boolean returnsCursor;                // 判断返回类型是Cursor类型吗
        private final Class<?> returnType;                  // 方法返回类型
        private final String mapKey;                        // 获得@MapKey注解里面的value值
        private final Integer resultHandlerIndex;           // 入参为ResultHandler类型的下标号
        private final Integer rowBoundsIndex;               // 入参为RowBounds类型的下标号
        private final ParamNameResolver paramNameResolver;  // 入参名称解析器

至此,就获取到了一个MapperMethod对象。

大致流程如下:

  1. 如果不是Object默认方法或default类型的方法,则会从cachedMapperMethod中以method为key获取MapperMethod对象
  2. 如果没有在cache中,会new MapperMethod对象并维护到cache中
  3. new MapperMethod对象里,会维护SqlCommand和MethodSignate对象,其中
    • SqlCommand用于保存MapperStatement的唯一标识id和sql的命令类型(

      MapperStatement:用于保存映射器的一个节点(select|insert|delete|update)。包括许多我们配置的SQL、SQL的id、缓存信息、resultMap、parameterType、resultType、languageDriver等重要配置内容。

    • MethodSignature主要维护方法签名,保存方法入参、返回值类型等等

第三阶段:根据SQL指令跳转执行语句

这个阶段主要将在代理的方法invoke中的最后一句代码:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ...
        /** 调用MapperMethod.execute()方法执行SQL语句 */
        return mapperMethod.execute(sqlSession, args);
	}

Mybatis 框架源码解读(详细流程图+时序图)
首先我们进入到excute()方法看看:

    /**
     * MapperMethod采用命令模式运行,根据上下文跳转,它可能跳转到许多方法中。实际上它最后就是通过SqlSession对象去运行对象的SQL。
     */
    // eg1: sqlSession = DefaultSqlSession@1953  args = {2L}
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        // eg1: command.getType() = SELECT
        switch (command.getType()) {
            case INSERT: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.insert(command.getName(), param));
                break;
            }
            case UPDATE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.update(command.getName(), param));
                break;
            }
            case DELETE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.delete(command.getName(), param));
                break;
            }
            case SELECT:
                // eg1: method.returnsVoid() = false  method.hasResultHandler() = false
                if (method.returnsVoid() && method.hasResultHandler()) {
                    executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (method.returnsMany()) { // eg1: method.returnsMany() = false
                    result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) { // eg1: method.returnsMap() = false
                    result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) { // eg1: method.returnsCursor() = false
                    result = executeForCursor(sqlSession, args);
                } else {
                    // eg1: args = {2L}
                    /** 将参数转换为sql语句需要的入参 */
                    Object param = method.convertArgsToSqlCommandParam(args);

                    // eg1: sqlSession=DefaultSqlSession  command.getName()="mapper.UserMapper.getUserById" param={"id":2L, "param1":2L}
                    /** 执行sql查询操作 */
                    result = sqlSession.selectOne(command.getName(), param);
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + command.getName());
        }
		...
        return result;
    }

根据不同的sql查询命令,进入不同的方法。

  1. 例子中,我们执行的是SELECT,所以会进入SELECT分支,返回的结果也是根据第二阶段获取的MethodSignature进行判断,是返回多条记录、单条记录还是map、Cursor类型,这里是单条记录
  2. 根据属性名与参值的映射关系,调用methodSignature实例的getNamedParams(args)方法进行对应,比如: param={"id": 2L, "param1", 2L}
  3. 执行sql查询操作:result = sqlSession.selectOne(command.getName(), param);

在第三阶段中

  1. 通过对mapperMethod中的methodSignature实例进行选择对应执行的方法
  2. 在select分支中对参数进行转换对应,准备开始执行selectOne()操作

第四阶段:查询前的缓存处理

这个阶段主要是对上个阶段的如下代码进行解析:

      /** 执行sql查询操作 */
      result = sqlSession.selectOne(command.getName(), param);

Mybatis 框架源码解读(详细流程图+时序图)
进入selectOne()方法实际调用的是DefaultSqlSession的方法:

    // eg1: statement="mapper.UserMapper.getUserById" parameter={"id":2L, "param1":2L}
    @Override
    public <T> T selectOne(String statement, Object parameter) {
        List<T> list = this.selectList(statement, parameter);
        ...
    }

	// 最终调用的是以下方法
    // eg1: statement="mapper.UserMapper.getUserById" parameter={"id":2L, "param1":2L}
    @Override
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            // eg1: statement="mapper.UserMapper.getUserById"
            MappedStatement ms = configuration.getMappedStatement(statement);
            // eg1: executor=CachingExecutor
            //      wrapCollection(parameter)=parameter={"id": 2L, "param1", 2L}
            //      rowBounds=RowBounds.DEFAULT=new RowBounds()
            //      Executor.NO_RESULT_HANDLER=null
            return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
  1. 需要根据之前sqlCommand中的id(id=“mapper.UserMapper.getUserById”)获取MappedStatement
  2. 然后调用CachingExcutor(带有sqlSession级别的缓存)的query方法
    // eg1: parameterObject={"id":2L, "param1":2L}
    //      rowBounds=new RowBounds()
    //      resultHandler=null
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
                             ResultHandler resultHandler) throws SQLException {
        // eg1: parameterObject = {"id":2L, "param1":2L}
        /** 获得boundSql对象,承载着sql和对应的参数*/
        BoundSql boundSql = ms.getBoundSql(parameterObject);
    
        // eg1: parameterObject = {"id":2L, "param1":2L}  rowBounds = new RowBounds()
        /** 生成缓存key */
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    
        // eg1: parameterObject = {"id":2L, "param1":2L}  rowBounds = new RowBounds() resultHandler = null
        /** 执行查询语句 */
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    
    • 获取BoundSql,承载着sql和对应的参数
    • 生成一、二级缓存需要的缓存key
    • 开始执行查询操作:query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)

执行查询操作:query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)

    // eg1: parameterObject = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
                             ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        Cache cache = ms.getCache();
        // eg1: cache = null
        /** 如果在UserMapper.xml配置了<cache/>开启了二级缓存,则cache不为null*/
        if (cache != null) {
            /**
             * 如果flushCacheRequired=true并且缓存中有数据,则先清空缓存
             *
             * <select id="save" parameterType="XXXXXEO" statementType="CALLABLE" flushCache="true" useCache="false">
             *     ……
             * </select>
             * */
            flushCacheIfRequired(ms);

            /** 如果useCache=true并且resultHandler=null*/
            if (ms.isUseCache() && resultHandler == null) {
                ensureNoOutParams(ms, parameterObject, boundSql);
                @SuppressWarnings("unchecked")
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    /** 执行查询语句 */
                    list = delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    /** 以cacheKey为主键,将结果维护到缓存中 */
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
            }
        }
        // eg1: delegate = SimpleExecutor(BaseExecutor) parameterObject = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
        return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

真正调用的是Executor的实现类BaseExecutor实例的query()方法

    // eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
    @SuppressWarnings("unchecked")
    @Override
    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());
        // eg1: closed = false
        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }

        // eg1: queryStack = 0  ms.isFlushCacheRequired() = false
        /** 如果配置了flushCacheRequired=true并且queryStack=0(没有正在执行的查询操作),则会执行清空缓存操作*/
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }

        List<E> list;
        try {
            /** 记录正在执行查询操作的任务数*/
            queryStack++; // eg1: queryStack=1

            // eg1: resultHandler=null localCache.getObject(key)=null
            /** localCache维护一级缓存,试图从一级缓存中获取结果数据,如果有数据,则返回结果;如果没有数据,再执行queryFromDatabase */
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            // eg1: list = null
            if (list != null) {
                /** 如果是执行存储过程 */
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                // eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            queryStack--;
        }
        if (queryStack == 0) {
            /** 延迟加载处理 */
            for (DeferredLoad deferredLoad : deferredLoads) {
                deferredLoad.load();
            }
            // issue #601
            deferredLoads.clear();

            // eg1: configuration.getLocalCacheScope()=SESSION
            /** 如果设置了<setting name="localCacheScope" value="STATEMENT"/>,则会每次执行完清空缓存。即:使得一级缓存失效 */
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                // issue #482
                clearLocalCache();
            }
        }
        return list;
    }
  1. 如果开启了缓存,则尝试从缓存中获取结果
  2. 如果缓存中没有结果,则执行查询操作
  3. 如果没有开启缓存,则直接执行查询操作,真正查询操作调用的是queryFromDatabase()

最后将查询的list结果返回,如果是selectOne(),只会list.get(0)

在第三阶段中,主要做了如下事情:

  1. 调用DefaultSession实例的selectOne(command.getName(), param);,获取在Configuration中对应的MappedStatement
  2. 在selectOne()中,会调用query()方法
    • 获得BoundSql(承载sql和参数的对象)
    • 生成一、二级缓存需要的缓存key
    • 执行查询操作(会尝试从缓存中获取)
    • 返回list.get(0)结果

第五阶段:执行DB查询操作(真正开始查询了)

在上一个阶段中,调用了执行sql的方法:

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

Mybatis 框架源码解读(详细流程图+时序图)

    // eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,
                                          ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        // eg1: key = -445449180:-48278933:mapper.UserMapper.getUserById:0:2147483647:select id, name, age from tb_user where id = ?:2:dev
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            // eg1: SimpleExecutor.doQuery parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            localCache.removeObject(key);
        }
        /** 将查询结果放到一级缓存中,如果同一session中有相同查询操作,则可以直接从缓存中获取结果*/
        localCache.putObject(key, list);

        // eg1: ms.getStatementType() = PREPARED
        if (ms.getStatementType() == StatementType.CALLABLE) {
            localOutputParameterCache.putObject(key, parameter);
        }
        return list;
    }

主要执行语句是list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);,而真正调用的是SimpleExecutor类的doQuery()

    // eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
                               BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            /** 根据Configuration来构建StatementHandler */
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds,
                    resultHandler, boundSql);

            // eg1: handler=RoutingStatementHandler
            /** 然后使用prepareStatement方法,对SQL进行预编译并设置入参 */
            stmt = prepareStatement(handler, ms.getStatementLog());

            // eg1: handler=RoutingStatementHandler parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null
            /** 开始执行真正的查询操作。将包装好的Statement通过StatementHandler来执行,并把结果传递给resultHandler */
            return handler.<E>query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

这里有三步:

  1. 根据Configuration来构建StatementHandler,并添加到拦截器链interceptorChain中

  2. 然后使用prepareStatement方法,对SQL进行预编译并设置入参

        /**
         * 使用prepareStatement方法,对SQL编译并设置入参
         *
         * @param handler
         * @param statementLog
         * @return
         * @throws SQLException
         */
        // eg1: handler=RoutingStatementHandler
        private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
            Statement stmt;
    
            /** 获得Connection实例 */
            Connection connection = getConnection(statementLog);
    
            // eg1: handler=RoutingStatementHandler
            /** 第一步:调用了StatementHandler的prepared进行了【sql的预编译】 */
            stmt = handler.prepare(connection, transaction.getTimeout());
    
            /** 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】 */
            handler.parameterize(stmt);
    
            // eg1: 返回org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570ded
            return stmt;
        }
    

    在handle.prepare()中执行编译语句,调用的是原生的JDBC进行预编译

        /**
     * 使用prepareStatement方法,对SQL编译并设置入参
     *
     * @param handler
     * @param statementLog
     * @return
     * @throws SQLException
     */
    // eg1: handler=RoutingStatementHandler
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
    
        /** 获得Connection实例 */
        Connection connection = getConnection(statementLog);
    
        // eg1: handler=RoutingStatementHandler
        /** 第一步:调用了StatementHandler的prepared进行了【sql的预编译】 */
        stmt = handler.prepare(connection, transaction.getTimeout());
    
        /** 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】 */
        handler.parameterize(stmt);
    
        // eg1: 返回org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570ded
        return stmt;
    }
    
    • 第一步:调用了StatementHandler的prepared进行了【sql的预编译】

      	    // eg1: delegate=PreparedStatementHandler
      /**
       * 执行预编译语句
       */
      @Override
      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
          ErrorContext.instance().sql(boundSql.getSql());
          Statement statement = null;
          try {
              // eg1: 调用PreparedStatementHandler的instantiateStatement
              statement = instantiateStatement(connection);
              setStatementTimeout(statement, transactionTimeout);
              setFetchSize(statement);
              return statement;
          } catch (SQLException e) {
              closeStatement(statement);
              throw e;
          } catch (Exception e) {
              closeStatement(statement);
              throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
          }
      }
        ```
      
    • 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】

      // eg1: org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570ded
      /**
       * 针对预处理语句,设置入参
       */
      @Override
      public void setParameters(PreparedStatement ps) {
          ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
      
          // eg1: parameterMappings[0] = ParameterMapping{property='id', mode=IN, javaType=class java.lang.Long, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}
          List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
          if (parameterMappings != null) {
              // eg1: parameterMappings.size() = 1
              for (int i = 0; i < parameterMappings.size(); i++) {
                  ParameterMapping parameterMapping = parameterMappings.get(i);
                  // eg1: parameterMapping.getMode() = IN
                  if (parameterMapping.getMode() != ParameterMode.OUT) {
                      Object value;
                      // eg1: propertyName="id"
                      String propertyName = parameterMapping.getProperty();
                      // eg1: boundSql.hasAdditionalParameter(propertyName) = false
                      if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                          value = boundSql.getAdditionalParameter(propertyName);
                      }
                      // eg1: parameterObject = 2
                      else if (parameterObject == null) {
                          value = null;
                      }
                      // eg1: parameterObject.getClass() = java.lang.Long.class   返回ture
                      else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                          // eg1: value = parameterObject = 2L
                          value = parameterObject;
                      } else {
                          MetaObject metaObject = configuration.newMetaObject(parameterObject);
                          value = metaObject.getValue(propertyName);
                      }
                      // eg1: typeHandler=class java.lang.Long
                      TypeHandler typeHandler = parameterMapping.getTypeHandler();
                      // eg1: jdbcType=null
                      JdbcType jdbcType = parameterMapping.getJdbcType();
                      // eg1: value=2L jdbcType=null
                      if (value == null && jdbcType == null) {
                          jdbcType = configuration.getJdbcTypeForNull();
                      }
                      try {
                          // eg1: typeHandler=BaseTypeHandler
                          /** 针对预处理语句,设置入参 */
                          typeHandler.setParameter(ps, i + 1, value, jdbcType);
                      } catch (TypeException e) {
                          throw new TypeException("Could not set parameters for mapping: " + parameterMapping +
                                  ". Cause: " + e, e);
                      } catch (SQLException e) {
                          throw new TypeException("Could not set parameters for mapping: " + parameterMapping +
                                  ". Cause: " + e, e);
                      }
                  }
              }
          }
      }
      
    • 开始执行真正的JDBC查询操作,将包装好的Statement通过StatementHandler来执行,并把结果传递给resultHandler

          // eg1: delegate = PreparedStatementHandler  resultHandler = null
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
          /** 最终还是使用JDBC去进行数据操作 */
          PreparedStatement ps = (PreparedStatement) statement;
      
          /** 执行查询操作 */
          ps.execute();
      
          // eg1: 封装结果集 resultSetHandler=DefaultResultSetHandler
          /** 将结果集进行封装 */
          return resultSetHandler.handleResultSets(ps);
      }
      

执行查询完成后,会将结果集进行处理封装resultSetHandler.handleResultSets(ps);,这里逻辑看第六阶段,最终返回一个list(POJO)并维护到一级缓存中,至此就完成了DB查询操作

第五阶段基本流程如下:

  1. 通过queryFromDatabase()方法执行查询操作
  2. 调用SimpleExecutor的doQuery()方法
    • 利用Configuration构建一个StatementHandler
    • 根据构建的StatementHandler执行prepare()方法对sql预编译
    • 调用DefaultParameterHandler实例的setParameters()方法对预处理的语句设置入参(也就是处理?的占位符)
    • 调用构建的StatementHandler(PreparedStatementHandler)执行查询操作ps.execute();,然后将结果集处理并返回return resultSetHandler.handleResultSets(ps);
  3. 根据获取的list集合维护到一级缓存中并返回处理后的结果POJO

第六阶段:针对ResultSet结果集转换为POJO

在上一个阶段,我们将结果集查出了,需要进行处理

     /** 将结果集进行封装 */
     return resultSetHandler.handleResultSets(ps);

Mybatis 框架源码解读(详细流程图+时序图)
我们来看下handleResultSets(Statement stmt)方法:

    /**
     * 处理数据库操作的结果集
     */
    // eg1: 执行到这里
    @Override
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

        final List<Object> multipleResults = new ArrayList<>();
        int resultSetCount = 0;

        /** 首先:获得执行后的结果集,并封装到ResultSetWrapper */
        ResultSetWrapper rsw = getFirstResultSet(stmt);

        /** 其次:如果rsw != null && resultMapCount < 1,则抛异常ExecutorException */
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size(); // eg1: resultMapCount = 1
        validateResultMapsCount(rsw, resultMapCount);

        // eg1: rsw不为空 resultMapCount=1 resultSetCount=0
        /** 第三步:处理结果集 */
        while (rsw != null && resultMapCount > resultSetCount) {
            // eg1: ResultMap resultMap=resultMaps.get(0);
            ResultMap resultMap = resultMaps.get(resultSetCount);

            /** 处理结果集, 存储在multipleResults中 */
            handleResultSet(rsw, resultMap, multipleResults, null);

            // eg1: rsw=null
            rsw = getNextResultSet(stmt);

            cleanUpAfterHandlingResultSet();
            resultSetCount++; // eg1: 自增后resultSetCount=1
        }

        String[] resultSets = mappedStatement.getResultSets();
        // eg1: resultSets = null
        if (resultSets != null) {
            while (rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                    handleResultSet(rsw, resultMap, null, parentMapping);
                }
                rsw = getNextResultSet(stmt);
                cleanUpAfterHandlingResultSet();
                resultSetCount++;
            }
        }

        // eg1: multipleResults.get(0).get(0) = User{id=2, name='muse2', age=24, userContacts=null}
        /** 返回结果 */
        return collapseSingleResultList(multipleResults);
    }
  1. 第一步:获得执行后的结果集,并封装到ResultSetWrapper中

     private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException 					{
        // eg1: rs != null
        /** 通过JDBC获得结果集ResultSet */
        ResultSet rs = stmt.getResultSet();
        while (rs == null) {
            if (stmt.getMoreResults()) {
                rs = stmt.getResultSet();
            } else {
                /**
                 * getUpdateCount()==-1,既不是结果集,又不是更新计数了.说明没的返回了。
                 * 如果getUpdateCount()>=0,则说明当前指针是更新计数(0的时候有可能是DDL指令)。
                 * 无论是返回结果集或是更新计数,那么则可能还继续有其它返回。
                 * 只有在当前指指针getResultSet()==null && getUpdateCount()==-1才说明没有再多的返回。
                 */
                if (stmt.getUpdateCount() == -1) {
                    // no more results. Must be no resultset
                    break;
                }
            }
        }
        // eg1: rs不为空,则将结果集封装到ResultSetWrapper中
        /** 将结果集ResultSet封装到ResultSetWrapper实例中 */
        return rs != null ? new ResultSetWrapper(rs, configuration) : null;
    }
    
  2. 第二步:从MappedStatement中获得ResultMap集合(就是列和属性的映射map)

  3. 第三步:处理结果集handleResultSet(rsw, resultMap, multipleResults, null);,最终会将结果存在multipleResults中

    // eg1: parentMapping = null
    /**
     * 处理结果集
     */
    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults,
                                 ResultMapping parentMapping) throws SQLException {
        try {
            // eg1: parentMapping = null
            if (parentMapping != null) {
                handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
            } else {
                // eg1: resultHandler = null
                if (resultHandler == null) {
                    // eg1: objectFactory = DefaultObjectFactory defaultResultHandler里面包含了一个空集合的ArrayList实例
                    /** 初始化ResultHandler实例,用于解析查询结果并存储于该实例对象中 */
                    DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                    /** 解析行数据 */
                    handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                    multipleResults.add(defaultResultHandler.getResultList());
                } else {
                    handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
                }
            }
        } finally {
            // eg1:
            /** 关闭ResultSet */
            closeResultSet(rsw.getResultSet());
        }
    }
    
    // eg1: parentMapping = null
    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,
                                RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        // eg1: resultMap.hasNestedResultMaps()=false
        /** 是否是聚合Nested类型的结果集 */
        if (resultMap.hasNestedResultMaps()) {
            ensureNoRowBounds();
            checkResultHandler();
            handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
            // eg1: parentMapping = null
            handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
    }
    
    // eg1: parentMapping = null
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap,
                                                   ResultHandler<?> resultHandler, RowBounds rowBounds,
                                                   ResultMapping parentMapping) throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    
        // eg1: skipRows里面没做什么事情
        /** 将指针移动到rowBounds.getOffset()指定的行号,即:略过(skip)offset之前的行 */
        skipRows(rsw.getResultSet(), rowBounds);
    
        // eg1: shouldProcessMoreRows(resultContext, rowBounds) = true    rsw.getResultSet().next() = true
        while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
            /** 解析结果集中的鉴别器<discriminate/> */
            ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
    
            /** 将数据库操作结果保存到POJO并返回 */
            Object rowValue = getRowValue(rsw, discriminatedResultMap);
    
            // eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null}  parentMapping = null
            /** 存储POJO对象到DefaultResultHandler中 */
            storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
    }
    

    handleRowValuesForSimpleResultMap()方法中主要做了如下处理:

    • 将数据库操作结果保存到POJO并返回
          /**
           * 将数据库操作结果保存到POJO并返回
           */
          private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
              final ResultLoaderMap lazyLoader = new ResultLoaderMap();
              /** 创建空的结果对象 */
              Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
      
              // eg1: rowValue=User{id=null, name='null', age=null, userContacts=null}   hasTypeHandlerForResultObject(rsw, resultMap.getType())=false
              if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
                  /** 创建rowValue的metaObject */
                  final MetaObject metaObject = configuration.newMetaObject(rowValue);
      
                  // eg1: foundValues = useConstructorMappings = false
                  boolean foundValues = this.useConstructorMappings;
      
                  // eg1: shouldApplyAutomaticMappings(resultMap, false) = true
                  /** 是否应用自动映射 */
                  if (shouldApplyAutomaticMappings(resultMap, false)) {
                      // eg1: applyAutomaticMappings(rsw, resultMap, metaObject, null)=true
                      /**
                       * 将查询出来的值赋值给metaObject中的POJO对象
                       */
                      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; // eg1: foundValues=true
                  }
      
                  // eg1: foundValues=true
                  foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      
                  // eg1: lazyLoader.size()=0   foundValues=true
                  foundValues = lazyLoader.size() > 0 || foundValues;
      
                  // eg1: foundValues=true  configuration.isReturnInstanceForEmptyRow()=false
                  /** configuration.isReturnInstanceForEmptyRow() 当返回行的所有列都是空时,MyBatis默认返回null。当开启这个设置时,MyBatis会返回一个空实例。*/
                  rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
              }
              return rowValue; // eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null}
          }
      
          // eg1: columnPrefix = null
          private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader,
                                            String columnPrefix) throws SQLException {
              this.useConstructorMappings = false; // reset previous mapping result
              final List<Class<?>> constructorArgTypes = new ArrayList<>();
              final List<Object> constructorArgs = new ArrayList<>();
      
              /** 创建一个空的resultMap.getType()类型的实例对象 */
              Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
      
              // eg1: resultObject=User{id=null, name='null', age=null, userContacts=null}
              //      hasTypeHandlerForResultObject(rsw, resultMap.getType()) = false
              /** 判断resultMap.getType()是否存在TypeHandler */
              if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
                  final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      
                  // eg1: propertyMappings={}
                  for (ResultMapping propertyMapping : propertyMappings) {
                      /** 如果是聚合查询并且配置了懒加载 */
                      if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                          resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration,
                                  objectFactory, constructorArgTypes, constructorArgs);
                          break;
                      }
                  }
              }
              // eg1: resultObject=User{id=null, name='null', age=null, userContacts=null} constructorArgTypes.isEmpty()=true
              this.useConstructorMappings = (resultObject != null && !constructorArgTypes.isEmpty()); // eg1: useConstructorMappings = false
      
              return resultObject; // eg1: resultObject=User{id=null, name='null', age=null, userContacts=null}
          }
      
          // eg1: constructorArgTypes={} constructorArgs={} columnPrefix=null
          private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes,
                                            List<Object> constructorArgs, String columnPrefix) throws SQLException {
              // eg1: resultType = vo.User.class
              /** 获得需要转换为POJO的类型 */
              final Class<?> resultType = resultMap.getType();
      
              /** 将POJO类型包装成MetaClass */
              final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
      
              // eg1: constructorMappings = {}
              final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
      
              // eg1: hasTypeHandlerForResultObject(rsw, resultType) = false
              /** 判断resultType是否存在TypeHandler */
              if (hasTypeHandlerForResultObject(rsw, resultType)) {
                  /** 创建原始的ResultObject,即:通过配置好的TypeHandler来创建 */
                  return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
              }
      
              // eg1: constructorMappings.isEmpty()=true
              else if (!constructorMappings.isEmpty()) {
                  /** 创建参数化的ResultObject */
                  return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
              }
      
              // eg1: resultType.isInterface()=false  metaType.hasDefaultConstructor()=true
              else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
                  // eg1: objectFactory=DefaultObjectFactory  resultType=vo.User.class
                  /** 使用objectFactory初始化User对象*/
                  return objectFactory.create(resultType); // eg1: 返回User{id=null, name='null', age=null, userContacts=null}
              }
      
              else if (shouldApplyAutomaticMappings(resultMap, false)) {
                  return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
              }
              throw new ExecutorException("Do not know how to create an instance of " + resultType);
          }
      
      在getRowValue()方法中调用了有对自动映射进行判断(shouldApplyAutomaticMappings()),如果开启了自动映射,会将列名与属性名进行映射,然后在结果集中取出对应的列名value,然后set到对应属性名中
          // eg1: columnPrefix=null
          private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
                                                 String columnPrefix) throws SQLException {
      
      
              /** 创建自动映射 */
              List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
              boolean foundValues = false;
      
              // eg1: autoMapping={UnMappedColumnAutoMapping("id", "id", LongTypeHandler@2397, false),
              //                   UnMappedColumnAutoMapping("name", "name", StringTypeHandler@2418, false),
              //                   UnMappedColumnAutoMapping("age", "age", IntegerTypeHandler@2433, false)}
              // 真正的赋值操作
              if (autoMapping.size() > 0) {
                  for (UnMappedColumnAutoMapping mapping : autoMapping) {
                      // eg1: mapping.column="id"      mapping.typeHandler=LongTypeHandler       value=2L
                      // eg1: mapping.column="name"    mapping.typeHandler=StringTypeHandler     value="muse2"
                      // eg1: mapping.column="age"     mapping.typeHandler=IntegerTypeHandler    value=24
                      final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
                      if (value != null) {
                          // eg1: foundValues = true
                          // eg1: foundValues = true
                          // eg1: foundValues = true
                          foundValues = true;
                      }
                      // eg1: configuration.isCallSettersOnNulls()=false  mapping.primitive=false
                      // eg1: configuration.isCallSettersOnNulls()=false  mapping.primitive=false
                      // eg1: configuration.isCallSettersOnNulls()=false  mapping.primitive=false
                      if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
                          // eg1: mapping.property="id"  value=2L
                          // eg1: mapping.property="name"  value="muse2"
                          // eg1: mapping.property="age"  value=24
                          metaObject.setValue(mapping.property, value);
                      }
                  }
              }
              return foundValues; // eg1: 返回true
          }
      
  4. 存储POJO对象到DefaultResultHandler和DefaultResultContext中

    // eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null}  parentMapping = null
    /**
     * 存储POJO对象到DefaultResultHandler中
     */
    private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,
                             Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
        // eg1: parentMapping = null
        if (parentMapping != null) {
            linkToParents(rs, parentMapping, rowValue);
        } else {
            // eg1: resultHandler里保存空size的ArrayList rowValue=User{id=2, name='muse2', age=24, userContacts=null}
            /** 将结果存储到DefaultResultHandler中 */
            callResultHandler(resultHandler, resultContext, rowValue);
        }
    }

    // eg1: resultHandler里保存空size的ArrayList rowValue=User{id=2, name='muse2', age=24, userContacts=null}
    /**
     *
	*/
	 @SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
	 private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,
	                                Object rowValue) {
	     resultContext.nextResultObject(rowValue);
	     // eg1: resultHandler=DefaultResultHandler
	     ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
	 }

至此,就完成最后的结果集处理操作

第六阶段主要流程如下:

  1. 调用handlerResultSet()方法处理结果集
  2. 获取到ResultSet并封装到ResultSetWrapper
  3. 然后处理结果集rsw和resultMap handleResultMap(rsw, resultMap, multipleResults, null)
    • 对行数据进行解析handlerRowValues()
      • 利用反射创建空的POJO对象
      • 将column和property进行自动映射或根据配置的ResultMap映射
      • 根据映射的集合进行遍历,从ResultSet中获取值并set到POJO对象中
      • 返回rowValue
    • 将最终的结果存储在multipleResults集合中
  4. 返回multipleResults

总结

大致的总流程如下图所示:
Mybatis 框架源码解读(详细流程图+时序图)

上一篇:Leecode32 longest-valid-parentheses


下一篇:STM32 嵌入式操作系统的进入 HardFault_Handler分析