MyBatis原理分析之四:一次SQL查询的源码分析

2023-06-20,,

上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的。

准备工作

Mybatis完成一次SQL查询需要使用的代码如下:

    ) {
    );
    ) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
    return null;
    }
    }

我们再看看selectList方法(实际上是调用该类的另一个selectList方法来实现的):

    public List selectList(String statement, Object parameter) {
    return selectList(statement, parameter, RowBounds.DEFAULT);
    }
    public List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    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();
    }
    }

第二个selectList的执行步骤如下:

(1)根据SQL的ID到配置信息中找对应的MappedStatement,在之前配置被加载初始化的时候我们看到了系统会把配置文件中的SQL块解析并放到一个MappedStatement里面,并将MappedStatement对象放到一个Map里面进行存放,Map的key值是该SQL块的ID。

(2)调用执行器的query方法,传入MappedStatement对象、SQL参数对象、范围对象(此处为空)和结果处理方式。

好了,目前只剩下一个疑问,那就是执行器到底怎么执行SQL的呢?

上面我们知道了,默认情况下是采用SimpleExecutor执行的,我们看看这个类的doQuery方法:

    public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    Statement stmt = null;
    try {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler);
    stmt = prepareStatement(handler);
    return handler.query(stmt, resultHandler);
    } finally {
    closeStatement(stmt);
    }
    }

doQuery方法的内部执行步骤:

(1) 获取配置信息对象。

(2)通过配置对象获取一个新的StatementHandler,该类主要用来处理一次SQL操作。

(3)预处理StatementHandler对象,得到Statement对象。

(4)传入Statement和结果处理对象,通过StatementHandler的query方法来执行SQL,并对执行结果进行处理。

我们看一下newStatementHandler到底做了什么?

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
    }

上面代码的执行步骤:

(1)根据相关的参数获取对应的StatementHandler对象。

(2)为StatementHandler对象绑定拦截器插件。

RoutingStatementHandler类的构造方法RoutingStatementHandler如下:

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
    switch (ms.getStatementType()) {
    case STATEMENT:
    delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
    break;
    case PREPARED:
    delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
    break;
    case CALLABLE:
    delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
    break;
    default:
    throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
    }

根据 MappedStatement对象的StatementType来创建不同的StatementHandler,这个跟前面执行器的方式类似。StatementType有STATEMENT、PREPARED和CALLABLE三种类型,跟JDBC里面的Statement类型一一对应。

我们看一下prepareStatement方法具体内容:

    private Statement prepareStatement(StatementHandler handler) throws SQLException {
    Statement stmt;
    Connection connection = transaction.getConnection();
    //从连接中获取Statement对象
    stmt = handler.prepare(connection);
    //处理预编译的传入参数
    handler.parameterize(stmt);
    return stmt;
    }

来源: http://chenjc-it.iteye.com/blog/1541947

null

MyBatis原理分析之四:一次SQL查询的源码分析的相关教程结束。

《MyBatis原理分析之四:一次SQL查询的源码分析.doc》

下载本文的Word格式文档,以方便收藏与打印。