Mapper 通过类名和方法名就可以匹配到配置的SQL。
Mapper 执行的过程是通过 Executor
,StatementHandler
,ParameterHandler
和 ResultHandler
来完成数据库操作和结果返回的。
四大对象
- Executor 代表执行器,由它来调度
StatementHandler
,ParameterHandler
和 ResultHandler
等来执行对应的 SQL。
- StatementHandler 数据库会话器,作用是使用数据库的 Statement(PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用。
- ParameterHandler 用于 SQL 对参数的处理。
- ResultHandler 是进行最后数据集(ResultSet)的封装返回处理的。
这四大对象的都是在 Configuration 类中创建的,从这可以看出 Configuration 类是 MyBatis 的核心。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class Configuration { public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); return (ParameterHandler) interceptorChain.pluginAll(parameterHandler); }
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); }
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); return (StatementHandler) interceptorChain.pluginAll(statementHandler); }
public Executor newExecutor(Transaction transaction) { return newExecutor(transaction, defaultExecutorType); }
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } return (Executor) interceptorChain.pluginAll(executor); }
|
执行器
执行器(Executor)是一个真正执行 Java 和 数据库交互的东西,起到至关重要的作用。
在 MyBatis 中存在三种执行器,默认在 MyBatis 的 Configuration 类中创建执行器对象,配置文件 setting 元素的属性是 defaultExecutorType。
- SIMPLE,简易执行器,默认的执行器。
- REUSE,是一种执行器重用预处理语句。
- BATCH,执行器重要语句和批量更新,它是针对批量专用的执行器。
创建执行器 Executor 源码逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } return (Executor) interceptorChain.pluginAll(executor); }
|
配置文件 setting 属性配置:
1 2 3 4 5 6 7
| <settings> <setting name="defaultExecutorType" value="SIMPLE"/> </settings>
<settings> <setting name="defaultExecutorType" value="BATCH"/> </settings>
|
MyBatis 会根据配置的类型去确定需要创建三种执行器中的哪一种,在创建对象后,会执行下面这样一行代码。
1
| interceptorChain.pluginAll(executor);
|
这就是 MyBatis 的插件,这里它将为我们构建一层层的动态代理对象。在调度真实的 Executor 方法之前执行配置插件的代码可以修改。
以SIMPLE执行器为例,看看执行器方法内部。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @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(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } }
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; } }
|
MyBatis 根据 Configuration 来构建 StatementHandler,然后使用 PreparedStatement 方法对 SQL 编译并对参数进行初始化。实现过程是它调用了 StatementHandler 的 prepare() 方法进行预编译和基础设置,然后通过 StatementHandler 的 parameterize() 来设置参数并执行,ResultHandler 再组装查询结果返回给调用方来完成一次查询。
数据库会话器
数据库会话器(StatementHandler)是专门处理数据库会话的。下面看看 MyBatis 如何创建 StatementHandler 的。
在 Configuration 类里创建 StatementHandler,很显然,创建的真实对象是 RoutingStatementHandler 对象,它实现了接口 StatementHandler,和 Executor 一样,用代理对象做一层层的封装。。
1 2 3 4 5 6 7 8
| public class Configuration { public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); return (StatementHandler) interceptorChain.pluginAll(statementHandler); } }
|
在初始化 RoutingStatementHandler 对象的时候它会根据上下文环境决定创建哪个 StatementHandler 对象。
RoutingStatementHandler 不是我们真实的服务对象,它是通过适配模式找到应的的 StatementHandler 来执行的。
StatementHandler 和 Executor 一样分为三种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); }
}
@Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { return delegate.prepare(connection, transactionTimeout); }
@Override public void parameterize(Statement statement) throws SQLException { delegate.parameterize(statement); }
@Override public void batch(Statement statement) throws SQLException { delegate.batch(statement); }
@Override public int update(Statement statement) throws SQLException { return delegate.update(statement); }
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.query(statement, resultHandler); }
@Override public <E> Cursor<E> queryCursor(Statement statement) throws SQLException { return delegate.queryCursor(statement); }
@Override public BoundSql getBoundSql() { return delegate.getBoundSql(); }
@Override public ParameterHandler getParameterHandler() { return delegate.getParameterHandler(); } }
|
数据会话器定义了一个对象的适配器 delegate,它是一个 StatementHandler 接口对象,构造方法根据配置来适配对应的 StatementHandler 对象。它的作用是给实现类对象的使用提供一个统一,简易的使用适配器。此为对象的适配模式,可以让我们使用现有的类和方法对象提供服务,也可以根据实际需求对象屏蔽一些方法,甚至加入新的服务。
以最常用的 PreparedStatementHandler 为例,看看 MyBatis 早怎么执行查询的。看看它的主要三个方法 prepare、parameterize、query 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public abstract class BaseStatementHandler implements StatementHandler {
@Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { 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); } } }
|
prepare: instantiateStatement 方法是对 SQL 进行预编译。还做了一些基础配置,如设置超时、获最的最大行数等。然后 Executor 调用 parameterize 方法去设置参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class PreparedStatementHandler extends BaseStatementHandler {
@Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.handleResultSets(ps); } }
|
在执行 query 方法前,参数和SQL已经被 prepare 预编译了,参数已经在 parameterize 方法设置了,接下来只是执行 SQL,然后返回结果就可以了。结果可以看到使用了 ResultHandler 进行封装和返回。
一条查询SQL执行过程:
- Executor 先调用 StatementHandler 的 prepare 方法预编译,同时设置一些基本运行的参数。
- 然后调用 parameterize 方法设置参数,完成预编译。
- 跟着就是执行查询,update() 也是这样,最后如果需要结果,就用 ResultSetHandler 封装结果返回给调用者。
参数处理器
MyBatis 通过参数处理器 ParameterHandler 对预编译语句进行参数设置,作用很明显,就是完成对预编译参数的设置。
1 2 3 4 5 6 7
| public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps) throws SQLException;
}
|
ParameterHandler 是个接口,提供了两个方法给重写,其中 getParameterObject() 方法的作用是返回参数对象,**setParameters()**作用是设置预编译 SQL 语句的参数。
MyBatis 为 ParameterHandler 提供了一个默认实现类 DefaultParameterHandler,看看其中 setParameters() 的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class DefaultParameterHandler implements ParameterHandler {
@Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); 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 = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }
}
|
从代码中可以看到它是从 parameterObject 对象中取参数,然后使用 typeHandler 进行参数处理,如果有显式设置,则会根据签名注册的 typeHandler 对参数进行处理。而 typeHandler 也是在 MyBatis 初始化的时候,注册在 Configuration 里面的,需要的时候可以直接拿来用。这样就完成了参数设置。
结果处理器
结果处理器 ResultSetHandler 是对结果集进行封装返回。
ResultSetHandler接口:
1 2 3 4 5 6 7 8 9
| public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
|
- handleResultSets:包装结果集。
- handleCursorResultSets:数据库内处理游标结果集。
- handleOutputParameters:处理存储过程输出参数。
MyBatis 同样提供了 ResultSetHandler 默认的实现类 DefaultResultSetHandler,默认情况下都是通过这个类进行处理。这个实现比较复杂,它涉及使用了 JAVASSIST 或者 CGLIB 作为延迟加载,然后通过 typeHandler 和 ObjectFactory 进行组装结果返回,而我们使用它时几乎不会需要改变它。