Mybatis源码剖析之Mybatis执行流程(传统方式)

预读

Mybatis在设计上,分为三层:接口层,数据处理层,框架支持层

在接口层中,分为
传统模式:通过sqlSession + statementId。
接口代理模式:sqlSession+mapper接口

准备

Mybatis源码剖析之Mybatis执行流程(传统方式)_第1张图片

Mybatis源码剖析之Mybatis执行流程(传统方式)_第2张图片

userMapper.xml

Mybatis源码剖析之Mybatis执行流程(传统方式)_第3张图片

mybatis核心配置文件

<configuration>

    <!--加载外部的properties文件-->
    <properties resource="jdbc.properties"></properties>

    <!--开启全局的二级缓存配置-->
<!--    <settings>-->

<!--        <setting name="lazyLoadingEnabled" value="true"/>-->
<!--    </settings>-->


    <!--给实体类的全限定类名给别名-->
    <typeAliases>
        <!--给单独的实体起别名-->
        <!--  <typeAlias type="com.xiaoxuu.pojo.User" alias="user"></typeAlias>-->
        <!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写-->
        <package name="com.xiaoxu.pojo"/>
    </typeAliases>


    <!--environments:运行环境-->
    <environments default="development">
        <environment id="development">
            <!--当前事务交由JDBC进行管理-->
            <transactionManager type="JDBC"></transactionManager>
            <!--当前使用mybatis提供的连接池-->
            <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>

    <!--引入映射配置文件-->
    <mappers>
        <!-- <mapper class="com.xiaoxuu.mapper.IOrderMapperr"></mapper>-->
        <package name="com.xiaoxu.mapper"/>
    </mappers>
</configuration> 

Mybatis传统方式执行流程

1、通过classLoader 读取核心配置文件 为输入流

在这里插入图片描述
Mybatis源码剖析之Mybatis执行流程(传统方式)_第4张图片

注意:只要是流,底层就是数组,那么就存在两个指针 一个position指向数组存储元素的后一个节点,一个limit指向尾节点

2、解析配置文件,封装Configuration对象 创建DefaultSqlSessionFactory对象

在这里插入图片描述
在这里插入图片描述

通过XMLConfigBuilder创建XMLConfigParser
在这里插入图片描述
通过parse正式解析XML为Configuration对象
Mybatis源码剖析之Mybatis执行流程(传统方式)_第5张图片

解析完后可以看到Configuration对象 包含了
数据源,事务管理器,解析到的Mappers,TypeHandler(数据库类型和JAVA类型互转),typeAlias(类型别名识别),mappedStatements(CURD标签对象),resultMaps(映射对象),cache等等

Mybatis源码剖析之Mybatis执行流程(传统方式)_第6张图片
Mybatis源码剖析之Mybatis执行流程(传统方式)_第7张图片

Mybatis源码剖析之Mybatis执行流程(传统方式)_第8张图片
在这里插入图片描述
Mybatis源码剖析之Mybatis执行流程(传统方式)_第9张图片

注意sql中的#{id},${id} 等占位符,全部会被解析到paramterMapping中
Mybatis源码剖析之Mybatis执行流程(传统方式)_第10张图片
最终返回SqlSessionFactory

Mybatis源码剖析之Mybatis执行流程(传统方式)_第11张图片

3、通过SqlSessionFactory创建sqlSession,创建事务对象Transaction,创建executor

在这里插入图片描述

注意executor,默认有三种 SImpleExecutor,ReuseExecutor,BatchExecutor。默认使用SImpleExecutor

在这里插入图片描述

在这里插入图片描述

3.1、获取全局环境,datasurce,事务管理器
Mybatis源码剖析之Mybatis执行流程(传统方式)_第12张图片

3.2、默认创建一个sqlsession,那么就是开启事务,创建事务对象Transaction

Mybatis源码剖析之Mybatis执行流程(传统方式)_第13张图片

3.3、通过指定的executor类型创建executor
在这里插入图片描述

3.4、创建SqlSession对象
在这里插入图片描述

4、根据statementId(namespace+method signatuae),从Configuration中map集合中获取到了指定的MappedStatement对象。然后通过executor执行对应的sql语句

在这里插入图片描述
从configuration中获取mappedStatement
Mybatis源码剖析之Mybatis执行流程(传统方式)_第14张图片
通过executor执行sql

Mybatis源码剖析之Mybatis执行流程(传统方式)_第15张图片

4.1、获取BoundSql(sql,sql上解析的参数,用户传入的sql参数)

 // 获得 BoundSql 对象
    public BoundSql getBoundSql(Object parameterObject) {
        // 获得 BoundSql 对象
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        // 忽略,因为  已经废弃,参见 http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html 文档
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings == null || parameterMappings.isEmpty()) {
            boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
        }

        // check for nested result maps in parameter mappings (issue #30)
        // 判断传入的参数中,是否有内嵌的结果 ResultMap 。如果有,则修改 hasNestedResultMaps 为 true
        // 存储过程相关,暂时无视
        for (ParameterMapping pm : boundSql.getParameterMappings()) {
            String rmId = pm.getResultMapId();
            if (rmId != null) {
                ResultMap rm = configuration.getResultMap(rmId);
                if (rm != null) {
                    hasNestedResultMaps |= rm.hasNestedResultMaps();
                }
            }
        }

        return boundSql;
    }

4.2、从mappedStatement中获取是否开启了缓存(二级缓存),如果开启了从缓存中(二级缓存中)获取,如果没有开启,从一级缓存中取

二级缓存存在于mapper,每一个mappedStatement都持有,一级缓存存在于sqlSession,因为一级缓存存在于SimpleExecutor,simpleExecutor存在于sqlSession中

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
            throws SQLException {

        // 从 MappedStatement 中获取 Cache,注意这里的 Cache 是从MappedStatement中获取的
        // 也就是我们上面解析Mapper中标签中创建的,它保存在Configration中
        // 我们在初始化解析xml时分析过每一个MappedStatement都有一个Cache对象,就是这里
        Cache cache = ms.getCache();

        // 如果配置文件中没有配置 ,则 cache 为空
        if (cache != null) {
            //如果需要刷新缓存的话就刷新:flushCache="true"
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                // 暂时忽略,存储过程相关
                ensureNoOutParams(ms, boundSql);
                @SuppressWarnings("unchecked")
                // 从二级缓存中,获取结果
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    // 如果没有值,则执行查询,这个查询实际也是先走一级缓存查询,一级缓存也没有的话,则进行DB查询
                    list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    // 缓存查询结果
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                // 如果存在,则直接返回结果
                return list;
            }
        }
        // 不使用缓存,则从数据库中查询(会查一级缓存)
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

4.3、从sqlSession持有的一级缓存中获取,获取不到从数据中查

 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());
        // 已经关闭,则抛出 ExecutorException 异常
        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }
        // 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }
        List<E> list;
        try {
            // queryStack + 1
            queryStack++;
            // 从一级缓存中,获取查询结果
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            // 获取到,则进行处理
            if (list != null) {
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            // 获得不到,则从数据库中查询
            } else {
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            // queryStack - 1
            queryStack--;
        }
        if (queryStack == 0) {
            // 执行延迟加载
            for (DeferredLoad deferredLoad : deferredLoads) {
                deferredLoad.load();
            }
            // issue #601
            // 清空 deferredLoads
            deferredLoads.clear();
            // 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                // issue #482
                clearLocalCache();
            }
        }
        return list;
    }

Mybatis源码剖析之Mybatis执行流程(传统方式)_第16张图片

4.4、从数据库中查询数据

// 从数据库中读取操作
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        // 在缓存中,添加占位对象。此处的占位符,和延迟加载有关,可见 `DeferredLoad#canLoad()` 方法
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            // 执行读操作
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            // 从缓存中,移除占位对象
            localCache.removeObject(key);
        }
        // 添加到缓存中
        localCache.putObject(key, list);
        // 暂时忽略,存储过程相关
        if (ms.getStatementType() == StatementType.CALLABLE) {
            localOutputParameterCache.putObject(key, parameter);
        }
        return list;
    }

4.5、数据库查询,先会获取statementHandler,其只要负责sql处理。而statementHandler有多种,默认使用PreparedStatementHandler。statementHandler 会设置boundSql(sql信息),parameterHandler(sql参数处理器),ResultSetHandler(结果集处理器),typeAlias,typeHandler(java与jdbc类型转换处理器),而这些处理器,在解析XML的时候已经存在,被封装到Configuration

Mybatis源码剖析之Mybatis执行流程(传统方式)_第17张图片

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 获得 Configuration 对象
        this.configuration = mappedStatement.getConfiguration();

        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;

        // 获得 TypeHandlerRegistry 和 ObjectFactory 对象
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();

        // 如果 boundSql 非空,一般是写类操作,例如:insert、update、delete ,则先获得自增主键,然后再创建 BoundSql 对象
        if (boundSql == null) { // issue #435, get the key before calculating the statement
            // 获得自增主键
            generateKeys(parameterObject);
            // 创建 BoundSql 对象
            boundSql = mappedStatement.getBoundSql(parameterObject);
        }
        this.boundSql = boundSql;

        // 创建 ParameterHandler 对象
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        // 创建 ResultSetHandler 对象
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
    }

4.6、数据库查询,获取到statementHandler之后会应用插件,实现了mybatis interceptor的类,执行

在这里插入图片描述

4.7、创建prepareStatement,prepareStatement通过datasource获取connection开启事务,设置sql参数,statementHandler执行prepareStatement,执行预编译sql。

Mybatis源码剖析之Mybatis执行流程(传统方式)_第18张图片

Mybatis源码剖析之Mybatis执行流程(传统方式)_第19张图片

创建connection的时候日志级别默认RR
Mybatis源码剖析之Mybatis执行流程(传统方式)_第20张图片

4.8、statementHandler通过prepareStatement执行sql之后,获取最后结果,通过resultSetHandler进行结果集封装,处理完后将结果放入一级缓存

Mybatis源码剖析之Mybatis执行流程(传统方式)_第21张图片

@Override
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

        // 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List 对象。
        // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。
        final List<Object> multipleResults = new ArrayList<>();

        int resultSetCount = 0;
        // 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象
        ResultSetWrapper rsw = getFirstResultSet(stmt);

        // 获得 ResultMap 数组
        // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount); // 校验
        while (rsw != null && resultMapCount > resultSetCount) {
            // 获得 ResultMap 对象
            ResultMap resultMap = resultMaps.get(resultSetCount);
            // 处理 ResultSet ,将结果添加到 multipleResults 中
            handleResultSet(rsw, resultMap, multipleResults, null);
            // 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象
            rsw = getNextResultSet(stmt);
            // 清理
            cleanUpAfterHandlingResultSet();
            // resultSetCount ++
            resultSetCount++;
        }

        // 因为 `mappedStatement.resultSets` 只在存储过程中使用,忽略即可
        String[] resultSets = mappedStatement.getResultSets();
        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++;
            }
        }

        // 如果是 multipleResults 单元素,则取首元素返回
        return collapseSingleResultList(multipleResults);
    }
 
  

Mybatis源码剖析之Mybatis执行流程(传统方式)_第22张图片

总结

Mybatis源码剖析之Mybatis执行流程(传统方式)_第23张图片

你可能感兴趣的:(Mybatis源码,Java源码,Mybatis,java,maven,Mybatis源码)