前面我们分析过Mybatis的Executor,其实执行器还是调用StatementHandler来执行数据库操作。Mybatis中有3种类型的hanlder,分别为StatementHandler、ParameterHandler、ResultSetHandler。ParameterHandler主要解析参数,为Statement设置参数,ResultSetHandler主要是负责把ResultSet转换成Java对象。下面我们通过个时序图来区分下各自的作用。
看完时序图后大家应该可以知道这几个Handler的作用了。
ParameterHandler主要是解析Sql参数和用户传入参数,然后2者进行关联并设置到PreparedStatement中去。我们先看下ParameterHandler类。
public interface ParameterHandler {
//获取参数方法
Object getParameterObject();
//把参数设置到PreparedStatement中
void setParameters(PreparedStatement ps)
throws SQLException;
}
子类实现类图
从上图可知,ParameterHandler只有一个实现类。我们现在分析下这个类。
// An highlighted block
public class DefaultParameterHandler implements ParameterHandler {
//参数类型转换器的注册表,如果参数的类型在参数类型转换器的注册表中存在,
//则会根据具体的转换类型进行转换,这块代码相对简单写自己看下就明白了。
private final TypeHandlerRegistry typeHandlerRegistry;
//MappedStatement简单理解就是我们一个查询,或者说是我们Mapper里面的一个方法。
//主要保存这我们的SQL信息,参数信息、resultMap信息等,他主要是对一个SQl的描素信息。
private final MappedStatement mappedStatement;
//查询的参数信息
private final Object parameterObject;
//保存着sql脚本,参数已经被替换掉的sql脚本
//参数的映射信息、参数信息
private final BoundSql boundSql;
//全局的configuration
private final Configuration configuration;
//构造方法
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
@Override
public Object getParameterObject() {
return parameterObject;
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从boundSql列表中获取所有的参数映射列表,
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)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
//MetaObject是Mybatis映射的核心,这里通过创建一个MetaObject对象,可以轻易的获取到对象的属性
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//获取参数类型处理器
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
//如果参数为空,获取jdbcType类型,这个会把java类型的null转成成数据库能识别的null
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//通过typeHandler把参数设置到ParameterStatement里面去。
//typeHandler里面有各种类型来处理数据类型的转换,有兴趣可以自己点进去看下。
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);
}
}
}
}
}
}
DefaultParameterHandler核心逻辑:1.解析Sql脚本配置的参数信息
2.解析传入参数
3.通过TypeHandler把2的参数进行对应的类型转换并设置到PreparedStatment中。
4.TypeHandler里面提供了各种数据转换规则,有兴趣自己可以去看看。
ResultSetHandler主要是对查询的结果集进行处理。简单理解为把ResultSet转换成Java bean的过程。
这里面设置到关联查询,1对1,多对多的关联查询。博主对关联查询不太熟悉,所以只讲简单的ResultSet转换成Java bean转换。其他想关联查询,1对1,多对多查询博主这边也研究不是很透,不能误人子弟。
我们先看看ResultSetHandler接口,我们先看下接口信息。
// 接口提供了3个方法,在绝大多数情况下我们只会用第一方法,博主对其他2个方法也没有过多研究。
public interface ResultSetHandler {
//我们99%的情况下都是使用这个方法来对结果集进行解析。下面我们会对这个方法进行分析。
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
我们先看下ResultSetHandler的实现类信息。
ResultSetHandler只有一个实现类就是DefaultResultSetHandler,单千万别小看了DefaultResultSetHandler,所有的结果集处理都是在这里完成。关联查询也都是在这里处理。下面我们对常用的结果集处理进行分析。
我们先看下调用的堆栈信息:从PreparedStatement到DefaultResultSetHandler解析的全过程。
// 整个调用涉及到的类型及方法信息,下面我们会对这些方法进行分析。下面会着重看下如何创建返回结果对象,解析结果集并设置值
org.apache.ibatis.executor.statement.PreparedStatementHandler#query
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSet
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValues
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyAutomaticMappings
// 这里是ResultHandler处理的入口
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//通过Statement获取到ResultSet,并包装成ResultSetWrapper
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取对应的Map映射信息
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//验证
validateResultMapsCount(rsw, resultMapCount);
//开始处理结果集合
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//处理结果集
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
```javascript
// 这里是创建结果集返回对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List> constructorArgTypes = new ArrayList>();
final List ``
//这里应该是存储过程的,暂不分析
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++;
}
}
return collapseSingleResultList(multipleResults);
}
最终会调用到applyAutomaticMappings方法,中间有对各种关联查询,1对1,1对多等处理,
// An highlighted block
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//获取参数和结果集的映射列表信息
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
//循环映射列表信息
for (UnMappedColumnAutoMapping mapping : autoMapping) {
//通过ResultSet获取到对应的列信息。
//Mybatis提供了丰富的TypeHandler来解析结果集。这里自己去看下
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
//通过metaObject设置属性,把数据库查询出来的结果集set到对象中。
//这里是居于反射来实现的。MetaObject用法不舒服的朋友可以去熟悉一下。
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
如果不懂MetaBObject的同学可以去科普一下。
前面已经讲过ParameterHandler、ResultSetHandler(这里代码比较复杂,讲得比较范)。ParameterHandler、ResultSetHandler都是为StatementHandler服务的。StatementHandler的实现比ParameterHandler、ResultSetHandler要多很多,我们先看下StatementHandler接口。
// 预处理
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
void parameterize(Statement statement)
throws SQLException;
//批处理方法
void batch(Statement statement)
throws SQLException;
//更新或删除操作
int update(Statement statement)
throws SQLException;
//查询
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
//流标查询
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
//绑定SQL信息、包括Sql,参数等
BoundSql getBoundSql();
///虎丘参数处理器
ParameterHandler getParameterHandler();
下面我们看下整体的结构图。
这个结构是不是看起来和前面讲的Executor很相似啊。没错都是套路。
StatementHandler:接口,定义了基本的方法。
RoutingStatementHandler:这个类就是根据不同的类型创建不同的StatementHandler,可以理解为简单工厂模式。
CallableStatementHandler:执行存储过程的StatementHandler。
PreparedStatementHandler:预处理的StatementHandler。
SimpleStatementHandler:简单处理器
上面三种处理器我们着重看下PreparedStatementHandler的query和update方法。
// 执行查询操作
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//这里的statement是经过DefaultParameterHandler处理过的statement
PreparedStatement ps = (PreparedStatement) statement;
//执行查询操作
ps.execute();
//ResultHandler对结果集ResultSet进行处理
return resultSetHandler.<E> handleResultSets(ps);
}
我们来看下更新方法
// 更新方法
public int update(Statement statement) throws SQLException {
//已经通过预编译的statement
PreparedStatement ps = (PreparedStatement) statement;
//执行更新操作
ps.execute();
//获取影响的行
int rows = ps.getUpdateCount();
//获取绑定的参数,这里是请求参数
Object parameterObject = boundSql.getParameterObject();
//获取Mapper文件配置是否要获取主键
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
//根据配置设置刚才更新或者新增数据的主键
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
Mybatis中的执行器实际是调用处理来进行JDBC处理。处理器分为3类:1.StatementHandler处理器,2.ParameterHandler处理器,3.ResultSetHandler处理器。
StatementHandler调用ParameterHandler进行参数解析和预编译处理。
StatementHandler调用ResultSetHandler进行结果集映射。