您的位置:首页 > 其它

Mybatis源码详解之接口方法被执行流程源码解析

2017-09-02 17:05 771 查看
   
4000
    与上一篇Mybatis源码解析的博客已经隔了好长一段时间,最近发生了一些乱七八糟糟心的事情,甚至每天加班,没来得及写点什么,最近一个月的学习是乱的一塌糊涂。

        接着上一篇的分析,上一篇完成了所有配置文件的解析,将各个配置文件都解析到一个叫Configuration的类里,这些就是接口方法可以被执行的元数据,任何一个方法的执行必然依赖于此。接口方法执行流程就是怎样使用这些元数据呢?

        还是以最开始的实例工程引入方法执行,相信已经走到这里了,也肯定知道接口方法的执行就是对接口的一个动态代理。

        还记得上一篇博客中addMapper函数,最终把接口注册到一个Map缓存中,即knownMappers。

        下面开始今天的分析:

(1)对接口实现动态代理

IPlayDao play = sqlSession.getMapper(IPlayDao.class);
主要就是这句,经过getMapper方法以后,返回的已经不是接口了,而是一个经过动态代理的代理类了。

public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
//没有什么值得分析的,前面两个类就是带着传入的接口层层传递
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//这里就是上一篇一个mapper.xml文件中,namespace接口注册的一个缓存,从这个缓存中用接口提取出代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
//动态代理的玄机就在这个工厂类里
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

     带着接口步步深入,看到了proxy工厂,没有什么难易理解的部分,主要就是那个缓存中取代理工厂,这是初始化中非常关键的一个点。还有就是sqlSession的传递,也算是个关键,这块儿应该也算一种命令模式,因为未来不知道要调用接口的什么方法,所有要执行该方法的流程最终需要借助于sqlSession的分配,所以此处带着它层层传递,sqlSession是规划执行接口方法的总管。接着看代理工厂是怎么对所有的接口统一处理的。

public T newInstance(SqlSession sqlSession) {
//sqlsession,接口都传入到了MapperProxy里,这个类就是实现InvocationHandler的类,methodCache这里传入一个空的参数
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//看到了动态代理的实例化部分,相当于接口被实例化了,这就是getMapper最终返回的一个接口代理
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
显然,所有的接口子这个工厂类里被代理了,下一次调用接口中的方法的时候,直接进入了MapperProxy类的invoke方法中了,MapperProxy类里也包含了所有用到的属性,知道是属于哪个接口的代理,还有sqlSession属性,该属性中包含有Configuration,Executor。下面接着分析这个MapperProxy类。

(2)动态代理的统一处理,invoke方法

Ball ball = play.selectBall("1");
调用接口的方法,直接跳转到MapperProxy类的invoke方法,具体看一下:

public class MapperProxy<T> implements InvocationHandler, Serializable {

private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;

public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//首先判断该方法所属的类是否是一个对象,如果是直接反射调用该方法,显然,这里传递进来的是接口,不会进入if分支
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//这里将传入的方法包装成为一个MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
//包装每个被调用的方法
private MapperMethod cachedMapperMethod(Method method) {
//这就是当时代理工厂传进来的空的methodCache参数,就是一个简单的Map缓存,提高效率,只要被调用过一次就不会重新包装,直接从缓存中取
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//方法包装
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}

}
这里就是动态代理的最关键类了,看起来是不是其实也很简单,下面看一下方法的包装里做了些什么。

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
//传入的三大参数构造出两个内部类,类似于初始化,封装资源的一个过程,后面分析一下这两个内部类
//这个是提取以前初始化时候注册的MappedStatement
this.command = new SqlCommand(config, mapperInterface, method);
//这个是对被调用方法的一个封装提取
this.method = new MethodSignature(config, method);
}
//SqlCommand构造函数
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {
//还记得在构建MappedStatement的过程中,有这么一句注册configuration.addMappedStatement(statement);
//这里就是在构造提取MappedStatement的Map中的key
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) {
ms = configuration.getMappedStatement(parentStatementName);
}
}
if (ms == null) {
throw new BindingException("Invalid bound statement (not found): " + statementName);
}
//提取出ms,初始化了些参数
name = ms.getId();//其实这个就是mapper.xml中每个sql节点的id,也就是方法名
type = ms.getSqlCommandType(); //这个是该条sql语句的标签,是insert|select|delete|update
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
//MethodSignature的构造函数,就是对调用方法的一个解剖封装,提取各项参数
public MethodSignature(Configuration configuration, Method method) throws BindingException {
this.returnType = method.getReturnType();
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
this.mapKey = getMapKey(method);
this.returnsMap = (this.mapKey != null);
this.hasNamedParameters = hasNamedParams(method);
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
}
这里其实也可以看作是一个初始化的过程,在执行方法之前,提取资源库中的各种该方法应用的资源

(3)开始方法调用的执行

在mapperProxy的invoke方法最后一句,即调用封装后的MapperMethod的execute方法,从这里展开了接口方法的具体执行

//传入sqlSession和待执行方法的参数(也就是sql语句中需要的参数(PrepareStatement的参数))
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//首先根据封装该方法时候所封装的type判断是什么类型的sql语句, 选择不同的方法处理
//insert
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
//update
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
//delete
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
//select   讲解以select为例子,各中sql语句的流程基本一样
} else if (SqlCommandType.SELECT == command.getType()) {
//判断是select语句以后,还有不同的情况,就是封装方法时候的另一个类,methodsigature中的字段再次判断判断
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//文中讲解以这个为例,这种情况多些
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
这里仍旧将sqlSession带在方法的参数里往下一级传递。

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
//作了一个简单处理,如果有多个参数,将其封装在一个Map里
Object param = method.convertArgsToSqlCommandParam(args);
//如果method有分页信息,进if分支,大多数情况不会用系统自带分页,都是自己写分页拦截器实现分页
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
//sqlSession开启了真正的运筹帷幄的时候了(也就是命令模式应用的时候了,封装了各种功能,可以统一处理增删改查)
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
终于到了sqlSession调用它方法的时候了。即将运筹它下面的四大对象来完成调用流程。

//第一个参数statement,忘记了没有,封装方法的时候(SqlCommand类中),就是接口名.方法名;
//第二个参数就是sql语句中缺的参数
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//用这个statement从资源类中取出MappedStatement,这个类包含了mapper.xml文件中一个sql节点的所有信息
MappedStatement ms = configuration.getMappedStatement(statement);
//executor是什么,执行器,他来调度其他三个StatementHandler,ParameterHandler,ResultHandler来执行sql语句,
//他是怎么来的呢,在初始化出来sqlSession的时候就newExecutor过了,如果不传入特殊的Executor,会有一个默认的执行器
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
解析来到了Executor中,执行BaseExecutor中的qurey方法

//前两个参数,一个是sql节点,一个是传入的参数,后两个为null
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//进来首先获取BoundSql,这个BoundSql是什么呢,他就是建立sql和参数的地方,调用ms的方法获取,下面具体看怎么获取
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

public BoundSql getBoundSql(Object parameterObject) {
//ms中没干什么,直接将其转入下一层,sqlSource中获取,还记得初始化收构建SqlSource吗,这里确实是一个难点,下面再进入到SqlSource类
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.size() <= 0) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}

// check for nested result maps in parameter mappings (issue #30)
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;
}
//这里是一个DynamicSqlSource的getBoundSql方法(mybatis中大多数的sql都是带有where,if等各种条件的也就是动态的)
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry<String,
f1ff
Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}
这里的逻辑有一点没一步步分析清楚,总之最后将各个条件合并,封装出一个BoundSql,其里面已经包含了sql语句执行的所有的完整内容,等后面会继续对这部分完善,逐步分析到底是怎么一步步将sql语句拼接,封装的。

    拿到boundSql以后调到另一个query方法中

//看这个方法,其实什么也没做,只是些逻辑判断或者安全处理,关键的一步就是又转到了另一个函数queryFromDatabase
@SuppressWarnings("unchecked")
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 (closed) throw new ExecutorException("Executor was closed.");
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
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--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
return list;
}
//这个方法也是些逻辑判断等简单处理,就不细纠了,咱们只是跟踪执行流程,下面看到doQuery方法, 这个就是Executor中真正做事的方法了
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
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;
}
上面一些流程没有什么实质性的内容,下面主要分析doQuery方法,这个方法是执行器中的真正方法了, 在baseExecutor中是个抽象方法,根据自己配置的Executor,执行不同的查询方法,大多数时候只需要使用默认执行器即可,即SimpleExecutor

/**
*
* @param ms 被执行方法对应的sql节点全部信息
* @param parameter sql参数
* @param rowBounds null
* @param resultHandler null
* @param boundSql  sql语句
* @return
* @throws SQLException
*/
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();
//这句创建出执行sql的关键类,整个流程会创建出ParameterHandler,ResultHandler,下面看详细流程
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
//各个参数于上面一样
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//RoutingStatementHandler不是我们真实服务的对象,它是用适配器模式找到对应的StatementHandler来执行,RoutingStatementHandler继承自StatementHandler,
//有一个StatementHandler属性,一种特殊的适配器模式,具体选择哪种还是靠简单工厂来选择
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//Mybatis插件,前面博客专门讲解过
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//简单工厂模式选择合适的StatementHandler,与三种执行器相对应,这里StatementHandler对应这jdbc的Statement,PrepareStatement.
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());
}

}
//对应JDBC的prepareStatement
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
//构造函数
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;

this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();

if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}

this.boundSql = boundSql;
//其他两大对象,参数和结果集处理器,创建的过程就不看了, 比较简单,Mybatis都有ParameterHandler和ResultSetHandler的default实现
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
newStatementHadler的过程将sql语句执行的前置条件都准备好了,到这里四大对象已经都初始化完成,下面就是利用四大对象来完成执行的时候了。下面看doQuery的后两句内容。

stmt = prepareStatement(handler, ms.getStatementLog());
其实执行sql语句,最终还是用jdk的jdbc来处理,前面只是为了便利对他的各种封装,下面又即将要看到他的庐山真面目了,其实所说的基础知识最重要,也就重要在这种地方,最终的本质肯定是万变不离其宗,扎实的基础会对理解框架的核心部分事半功倍。

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
//这个就是jdbc的statement,这里就看到了熟悉的味道
Statement stmt;
//connection,从我们配置文件初始化中初始化的dataSource取得连接,具体和jdbc的处理一样
Connection connection = getConnection(statementLog);
//用connection创建statement
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
//BaseStatementHandler
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//创建statement,Statement和PrepareStatement的创建略有不同,后者在创建的时候
//需要将sql语句模板传进去,也就是两者使用sql的时机不同,这个方法是个抽象方法,根据不同的StatementHandler
//选择不同的处理方式,下面具体看
statement = instantiateStatement(connection);
setStatementTimeout(statement);
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);
}
}
创建Statement,下面看下instantiateStatement方法

//SimpleStatementHandler中的是实现,这个没什么逻辑直接创建Statement
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
//PreparedStatementHandler中的实现,这个创建PrepareStatement提前需要sql
protected Statement instantiateStatement(Connection connection) throws SQLException {
//从boundSql中取得sql
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
逻辑也比较简单,只要对JDBC的逻辑过程比较熟悉,这里都是一样的,经过这个方法以后就从Connection中取到了Statement或者PreparedStatement

接着就是像jdbc一样设置sql模板的参数值,当然如果是Statement的话就不需要设置了,看下Mybatis是怎么处理的。

handler.parameterize(stmt);

这里就是为sql设置参数,下面看下不同的参数Handler是怎么处理的    

//SimpleStatementHandler中不需要设置,空函数
public void parameterize(Statement statement) throws SQLException {
// N/A
}
//PreparedStatementHandler
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
//newParameterHandler的时候初始化了DefaultParameterHnadler,这里用初始化时候的参数组装sql中的参数之
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//取得sql语句和类对应的数据类型
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 metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
下面就剩下最后一步,执行sql语句了,不论是prepareStatement还是Statement都统一处理完了sql语句,剩下执行了

handler.<E>query(stmt, resultHandler);
大家发现没有,之前的所有逻辑是在Executor中处理,后来所有的这些组装过程都是在StatementHandler中处理的,有种感觉就是Executor包含者StatementHandler,将逻辑转到StatementHandler以后,感觉StatementHandler中包含着ParameterHandler和ResultSetHandler.

public <E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleResultSets(statement);
}
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
比较简单,就不分析了,直接调用jdbc的函数,执行sql语句,对sql语句的执行结果Mybatis又进行了封装,也就是ResultSetHandler的工作开始了
public List<Object> handleResultSets(Statement stmt) throws SQLException {
final List<Object> multipleResults = new ArrayList<Object>();

int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);

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

String[] resultSets = mappedStatement.getResulSets();
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);
}
       最终封装为一个list返回了,到这里整个方法的执行就结束了,其实看起来整个逻辑也没这么复杂,最后的关键部分都是对jcbc的封装,确实基础很关键。还有比较关键的一个部分就是sql语句的拼接部分,也就是BoundSql的产生过程,后面还值得分析理解。
      做一个对上面过程简单的总结梳理:SqlSession是通过Executor创建StatementHandler来运行的,而StatementHandler经过下面三步来执行sql

 (1)prepared预编译SQL;

 (2)parameterize设置参数;

 (3)query执行sql

     parameterize是调用parameterHandler的方法去设置的,而参数是根据类型处理器typeHandler去处理的。query方法是通过resultHandler进行结果封装的,如果是update直接返回证书,否则它通过typeHandler处理结果类型,然后用ObjectFactory提供的规则组装对象,返回给调用者。

最后QQ交流群(341252823),新建的,目前没有人,期望你的加入,可以一起讨论一些问题

下一篇文章准备写一下Mybatis和Spring的连接部分。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: