前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mybatis 源码分析(二)之 Mybatis 操作数据库的流程

Mybatis 源码分析(二)之 Mybatis 操作数据库的流程

作者头像
zoro
发布2019-04-11 15:23:01
1K0
发布2019-04-11 15:23:01
举报
文章被收录于专栏:Java开发者Java开发者

Mybatis 源码分析(二)之 Mybatis 操作数据库的流程

Mybatis系列:

Mybatis 基础介绍与逆向工程的构建 :https://cloud.tencent.com/developer/article/1412617

Mybatis 源码分析(一)之 Mybatis 的Executor的初始化:https://cloud.tencent.com/developer/article/1412636

Mybatis 源码分析(二)之 Mybatis 操作数据库的流程 :https://cloud.tencent.com/developer/article/1412614

Mybatis 源码分析(三)之 Mybatis 的一级缓存和二级缓存 :https://cloud.tencent.com/developer/article/1412629


承接之前的 Mybatis 源码分析(一)之 Mybatis 的Executor的初始化 继续进行之后的源码分析。

在之前的分析中,我们知道初始化Executor的流程。

代码语言:javascript
复制
SqlSessionFactoryBuilder->parse->Configuation->build->SqlSessionFactory->openSession->SqlSession->query->Executor

接下来则是mybatis操作数据库的流程


执行查询

代码语言:javascript
复制
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
    
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5;
    try {
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception var9) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
    } finally {
        ErrorContext.instance().reset();
    }

    return var5;
}

其中

代码语言:javascript
复制
MappedStatement ms = this.configuration.getMappedStatement(statement);

getMappedStatement是个map进行get,statement是我们传入的selectByPrimaryKey等参数,如下中的id。

代码语言:javascript
复制
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from food
    where id = #{id,jdbcType=INTEGER}
</select>

进行进入query

代码语言:javascript
复制
org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)

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 {
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator i$ = this.deferredLoads.iterator();

            while(i$.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                this.clearLocalCache();
            }
        }

        return list;
    }
}

其中

代码语言:javascript
复制
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;

传入的参数key,就是我们拼出来的cache key : cache key: id + sql + limit + offset

例如:

代码语言:javascript
复制
-2061571805:4913609797:com.demo.mybatis.mapper.UserMapper.selectByPrimaryKey:0:2147483647:select 
 
id, name, age

from user
where id = ?:1:development

假如第一次查询没有缓存,则查询数据库

代码语言:javascript
复制
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

继续进去

代码语言:javascript
复制
org.apache.ibatis.executor.BaseExecutor#queryFromDatabase

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 {
         //查询数据
        list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        //删除占位的
        this.localCache.removeObject(key);
    }

    //将查询出来的结果存入一级缓存
    this.localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        //如果是存储过程把参数存入localOutputParameterCache
        this.localOutputParameterCache.putObject(key, parameter);
    }

    return list;
}

进行数据查询

代码语言:javascript
复制
org.apache.ibatis.executor.SimpleExecutor#doQuery

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;

    List var9;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var9 = handler.query(stmt, resultHandler);
    } finally {
        this.closeStatement(stmt);
    }

    return var9;
}

进入query

代码语言:javascript
复制
org.apache.ibatis.executor.statement.PreparedStatementHandler#query

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement)statement;
    ps.execute();
    return this.resultSetHandler.handleResultSets(ps);
}

其中

代码语言:javascript
复制
 ps.execute();

底层仍然是我们熟悉的JDBC

代码语言:javascript
复制
java.sql.PreparedStatement#execute

然后进入handleResultSets对我们的结果信息进封装,最后返回给我们的,就是我们定义的pojo

代码语言:javascript
复制
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets

public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
    List<Object> multipleResults = new ArrayList();
    int resultSetCount = 0;
    ResultSetWrapper rsw = this.getFirstResultSet(stmt);
    List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    this.validateResultMapsCount(rsw, resultMapCount);

    while(rsw != null && resultMapCount > resultSetCount) {
        ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
        this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
        rsw = this.getNextResultSet(stmt);
        this.cleanUpAfterHandlingResultSet();
        ++resultSetCount;
    }

    String[] resultSets = this.mappedStatement.getResulSets();
    if (resultSets != null) {
        while(rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
            }

            rsw = this.getNextResultSet(stmt);
            this.cleanUpAfterHandlingResultSet();
            ++resultSetCount;
        }
    }

    return this.collapseSingleResultList(multipleResults);
}

其中

代码语言:javascript
复制
private List<Object> collapseSingleResultList(List<Object> multipleResults) {
    return multipleResults.size() == 1 ? (List)multipleResults.get(0) : multipleResults;
}

调用链

代码语言:javascript
复制
org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(java.lang.String, java.lang.Object)
 >org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(java.lang.String, java.lang.Object)
   >org.apache.ibatis.session.Configuration.getMappedStatement(java.lang.String)
    >org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
     >org.apache.ibatis.executor.CachingExecutor.createCacheKey 创建缓存的key
      >org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
         >org.apache.ibatis.executor.BaseExecutor.queryFromDatabase
           >org.apache.ibatis.executor.BaseExecutor.doQuery
             >org.apache.ibatis.executor.statement.PreparedStatementHandler.query
               >org.apache.ibatis.executor.resultset.ResultSetHandler.handleResultSets
                >org.apache.ibatis.executor.resultset.DefaultResultSetHandler

上面是操作数据库的流程,其中真正干活的是handler,mybatis底层操作数据库的仍然是我们熟悉的JDBC。

其中获取mapper的过程

代码语言:javascript
复制
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}

其中

代码语言:javascript
复制
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);

然后

代码语言:javascript
复制
org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper

public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }
    
    
org.apache.ibatis.session.Configuration#getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}

org.apache.ibatis.binding.MapperRegistry#getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

调用链

代码语言:javascript
复制
org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper
   >org.apache.ibatis.session.Configuration.getMapper
    >org.apache.ibatis.binding.MapperRegistry.getMapper
      >org.apache.ibatis.binding.MapperProxyFactory.newInstance(org.apache.ibatis.session.SqlSession)
        >org.apache.ibatis.binding.MapperMethod.execute

其中获取sql的过程

代码语言:javascript
复制
org.apache.ibatis.binding.MapperMethod#execute

public Object execute(SqlSession sqlSession, Object[] args) {
    Object param;
    Object result;
    if (SqlCommandType.INSERT == this.command.getType()) {
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
    } else if (SqlCommandType.UPDATE == this.command.getType()) {
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
    } else if (SqlCommandType.DELETE == this.command.getType()) {
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
    } else if (SqlCommandType.SELECT == this.command.getType()) {
        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 {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
        }
    } else {
        if (SqlCommandType.FLUSH != this.command.getType()) {
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        result = sqlSession.flushStatements();
    }

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

然后

代码语言:javascript
复制
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)

public <T> T selectOne(String statement, Object parameter) {
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
        return list.get(0);
    } else if (list.size() > 1) {
        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
        return null;
    }
}

org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
    return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

最后

代码语言:javascript
复制
org.apache.ibatis.mapping.MappedStatement#getBoundSql

public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = this.sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
        boundSql = new BoundSql(this.configuration, boundSql.getSql(), this.parameterMap.getParameterMappings(), parameterObject);
    }

    Iterator i$ = boundSql.getParameterMappings().iterator();

    while(i$.hasNext()) {
        ParameterMapping pm = (ParameterMapping)i$.next();
        String rmId = pm.getResultMapId();
        if (rmId != null) {
            ResultMap rm = this.configuration.getResultMap(rmId);
            if (rm != null) {
                this.hasNestedResultMaps |= rm.hasNestedResultMaps();
            }
        }
    }

    return boundSql;
}

调用链

代码语言:javascript
复制
org.apache.ibatis.binding.MapperMethod.execute
>org.apache.ibatis.session.SqlSession.selectOne(java.lang.String, java.lang.Object)
  >org.apache.ibatis.executor.BaseExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
    >org.apache.ibatis.mapping.MappedStatement.getBoundSql
总结

整个流程如下:

代码语言:javascript
复制
SqlSessionFactoryBuilder->parse->Configuation->build->SqlSessionFactory
->openSession->SqlSession->query->Executor->newStatementHandler->
StatementHandler->handlerResultSets->ResultSetHandler

感觉还有些没有写完整,不定期进行补充:)

如有问题,欢迎留言:)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.01.31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Mybatis 源码分析(二)之 Mybatis 操作数据库的流程
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档