想要全面深入的理解mybatis框架,首要前提是学习它的核心组件,我们先来看看MyBatis的“表面现象”——mybatis的核心组件包括:SqlSessionFactoryBuilder(构造器)、SqlSessionFactory(工厂接口)、SqlSession(会话接口)、SQL Mapper(映射器)。通过一张图来整体展示四个核心组件的关系:
使用MyBatis首先是使用配置或者代码去生成SqlSessionFactory,而MyBatis提供了构造器SqlSessionFactoryBuilder,它采用Builder模式来负责构建SqlSessionFactory,通过源码分析,该类下提供了多个build的重载方法。
但真正重载build方法只有三种:InputStream(字节流)、Reader(字符流)、Configuration(类)。在MyBatis中既可以通过读取XML文件的形式生成SqlSessionFactory,也可以通过Java代码的形式去生成SqlSessionFactory。一般我们常用的是读取XML配置文件的形式。读取XML配置文件的方式构造SqlSessionFactory,构造过程中注入了Configuration的实例对象,之后Configuration实例对象解析XML配置文件来构建SqlSessionFactory:
SqlSessionFactory sqlSessionFactory = null;
String resource = "mybatis-config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
在MyBatis读取XML配置文件后,通过Configuration类对象构建整个MyBatis的上下文,而Configuration采用的是单例模式 (SqlSessionFactory唯一的作用就是生产SqlSession,所以它的责任是唯一的)几乎所有的MyBatis配置都存放在这个单例对象中,以便后续使用。
在MyBayis中SqlSessionFactory是一个接口,它存在两个实现类:和DefaultSqlSessionFactory。SqlSessionManager多使用在多线程环境中。它的具体实现依靠的是DefaultSqlSessionFactory,它们之间的关系如下:
这种创建方式是一种Builder模式,对于复杂的对象而言,使用构造参数很难实现。这时使用一个类(Configuration)作为统领,一步步地构建所需的内容,然后通过它去创建最终的对象(SqlSessionFactory)。
在构建SqlSessionFactory中,Configuration是最重要的,它的作用是:
显然Configuration不是一个简单的类,MyBatis的配置信息都来源于此,Configuration是通过XMLConfigBuilder去构建的,它会读出所有的XML配置信息,把它们解析并保存在Configuration单例中。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse()); // 开始进行解析了 :)
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
}
}
}
当XMLConfigBudiler解析XML时,会将每一个SQL和其配置的内容保存起来,在MyBatis中一条SQL和它相关的配置信息是由3部分组成的,它们分别是MappedStatement、SqlSource和BoundSql。
通过以上分析,知道了MyBatis会根据文件流生成Configuration对象,进而构建SqlSessionFactory对象得到SqlSession对象了。得到SqlSession对象后就是去执行sql语句。
以上我们构建了SqlSessionFactory,在MyBtis中SqlSession是其核心接口,它有两个实现类,DefaultSqlSession和SqlSessionManager。DefaultSqlSession是单线程使用的,而SqlSessionManager在多线程环境下使用。SqlSession的作用类似于JDBC中的Connection对象,代表着一个连接资源的启用,它的作用:
前面我们构建了SqlSessionFactory,有了SqlSessionFactory创建的SqlSession就十分简单了。如下:
SqlSession sqlSession= sqlSessionFactory.openSession();
继续往下看,DefaultSqlSessionFactory实现了openSession()方法,之后调用了openSessionFromDataSource() 方法。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//传入的configuration获取环境变量对象、Environment可以配置多个环境配置
final Environment environment = configuration.getEnvironment();
//从环境对象中获取事务工厂对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//根据DataSource、事务隔离级别、自动提交创建事务对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//新建执行者
final Executor executor = configuration.newExecutor(tx, execType);
//创建默认SqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
//异常情况关闭事务
closeTransaction(tx); // may have fetched a connection so lets call close() (可能已经获取到数据库连接,因此执行关闭)
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
//重置错误上下文
ErrorContext.instance().reset();
}
}
最后调用了new DefaultSqlSession()创建了默认SqlSession。
SqlSession是一个接口,使用它并不复杂,它给出了查询、插入、更新、删除的方法。但运行过程是MyBatis中最难理解的部分。
先来看看MyBatis的源码是如何实现getMapper方法的。在MyBatis中,通过MapperProxy动态代理Dao, 也就是说当执行自己写的Dao里面的方法的时候,其实是对应的mapperProxy在代理。
通过SqlSession从Configuration中获取
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
显然应用到了Configuration对象的getMapper方法来获取对应接口对象,所以继续往下看。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
它有应用到了映射器的注册器Mapperregistry来获取对应的接口对象。
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
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);
}
}
这里首先它判断是否注册一个Mapper,如果没有则会抛出异常信息,如果有就会启用MapperProxyFactory工厂来生成一个代理实例。
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//动态代理我们写的 Dao接口
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
在这里可以看到Mapper映射是通过动态代理来实现对接口的绑定的,它的作用就是生成动态代理对象,而代理的方法则被放到了MapperProxy类中。我们再往下看MapperProxy的源码。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
这里可以看到,如果Mapper是一个JDK动态代理对象,那么它就会运行到invoke方法里面。invoke首先判断是否是一个类,这里Mapper是一个接口不是一个类,所以判定失败。然后生成MapperMethod对象,它是通过cachedMapperMethod方法对其初始化的。最后执行execute方法,把SqlSession和当前运行的参数传递进去。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
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 if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
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;
}
execute()方法主要的作用其实就是先判断CRUD类型,然后根据类型去选择到底执行sqlSession中的哪个方法,代码比较长,我们主要看一下查询最常用的方法executeForMany()方法。
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
//注意这里
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
//还有这里
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;
}
注意selectList()方法。
public <E> List<E> 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();
}
}
实际上最后还是回到了SqlSession,然后将CRUD交给了executor()去处理。
至此MyBatis为啥只用Mapper接口就能够运行已经清楚了,因为Mapper的XML文件的命名空间对应的是这个接口的全限定名,而方法就是那条SQL的id。这样MyBatis就可以通过全路径和方法名,将其和代理对象绑定起来。通过动态代理技术,让这个接口运行起来了,而后采用命令模式。最后使用SqlSession接口的方法使得它能够执行对应的SQL。
映射器就是一个动态代理对进入到了MapperMethod的execute方法,然后它经过判断就进入了SqlSession的delete、update、insert、select等方法。而实际上SqlSession的执行过程是通过Executor、StatementHandler、ParameterHandler和ResultHandler来完成数据库的操作和结果返回的。
执行器是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护,在Mybatis中存在三种执行器:
在MyBatis中,生成SqlSession的过程中,在DefaultSqlSessionFactory中调用openSession时,又调用了openSessionFromDataSource方法,生成了DefaultSession完成SqlSession的创建的。在openSessionFromDataSource方法中,通过Configuration的newExecutor生成了Executor对象
DefaultSqlSessionFactory中openSessionFromDataSource方法如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);//生成执行器
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
Configration的newExecutor方法中根据类型判断创建那种执行器,默认使用的是SimpleExecutor:
public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : 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);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
以上MyBatis创建了Executor,那么它是如何去处理CRUD操作的呢?之前的executeForMany()方法中调用了selectList()方法,而在selectList()方法中,源码如下:
public <E> List<E> 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) {
......
}
}
executor.query()通过一层层的调用最后来到了doQuery() 方法,源码如下:
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());
//StatementHandler封装了Statement, 让 StatementHandler 去处理
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
prepareStatement方法:
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根据Configeration来构建了StatementHandler,然后使用prepareStatement方法,对SQL编译和参数进行初始化,这样焦点又转移到了StatementHandler对象上。
数据库会话器封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等
在MyBatis生成StatementHandler源码如下:
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);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
显然创建的真实对象是RoutingStatementHandler的对象,它实现了接口StatementHandler 。RoutingStatementHandler并不是真正的服务对象,而是通过适配器模式找到对应的StatementHandler来执行的。和Executor一样,mybatis提供了三种会话处理器:
在初始化RoutingStatementHandler对象时,它会根据上下文环境来决定创建哪个具体的StatementHandler实例,源码如下:
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());
}
}
它定义了一个对象的适配器——delegate,然后构造方法根据配置来适配对应的StatementHandler对象,三种Handler并没有直接实现StatementHandler,而是继承了BaseStatementHandler,BaseStatementHandler中实现了StatementHandler接口,默认情况下是用PreparedStatementHandler(在MappedStatement的Builder方法里设置)。
Executor执行查询时会执行StatementHandler的prepare、parameterize和query方法,其中PreparedStatementHandler的prepare方法代码如下:
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 {
//这里初始化JDBC的Statement对象,instantiateStatement()需要由 BaseStatementHandler的子类实
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);
}
}
}
instantiateStatement()方法是对SQL进行了预编译,然后做一些基础配置,比如超时、获取的最大行数等。Executor中会调用它的parameterize()方法去设置参数,如下:
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
这个时候它就调用了PreparedStatement去完成,此时我们先看一下查询的方法——SimpleExecutor中的doQuery()方法中调用的query()方法执行SQL返回结果,如下:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
在执行前参数和SQL都被prepare()方法预编译了,参数在parameterize()方法中已经进行了设置,所以只要执行SQL,然后返回结果集就可以了。执行后可以看到ResultSetHandler对结果集的封装和返回。
负责对用户传递的参数转换成JDBC Statement 所对应的数据类型,完成对预编译参数得设置。
ParameterHandler只是一个接口,它有一个实现类DefaultParameterHandler
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
DefaultParameterHandler的setParameters方法中,可以看到从parameterMapping中拿到了typeHandler类型处理器,然后设置参数。
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
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());
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();
}
try {
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);
}
}
}
}
}
}
TypeHandler用于实现JAVA类型和JDBC类型的相互转换,它会根据参数的JAVA类型和JDBC类型选择合适的TypeHandler,再通过TypeHandler进行参数设置,以此达到JAVA类型到JDBC类型的转换。而TypeHandler也是在MyBatis初始化时,注册到Configuration里面的,需要时就可以直接拿过来用了,MyBtais就是通过这样完成参数设置的。
负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
回到PreparementStatementHandler类,以query方法为例,方法调用了resultSetHandler. handleCursorResultSets(ps)方法返回结果集
public class PreparedStatementHandler extends BaseStatementHandler {
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
@Override
public void batch(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.addBatch();
}
//对结果集处理
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleCursorResultSets(ps);
}
.....
}
resultSetHandler变量的定义在BaseStatementHandler中:
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;//结果集处理器
protected final ParameterHandler parameterHandler;//参数处理器
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;
...
}
ResultSetHandler也只是一个接口,handleResultSets()方法,它是用来包装结果集的,在它的子类DefaultResultSetHandler中实现:
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;
}
@Override
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;
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.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);
}
SqlSession是通过执行器Executor调度StatementHandler来运行的。而StatementHandler经过3步:
其中,parameterize是调用parameterHandler的方法设置的,而参数是根据类型处理器TypeHandler处理的。query/update方法通过ResultSetHandler进行处理结果的封装,如果是update语句,就返回参数,否则就通过TypeHandler处理结果类型,然后用ObjectFactory提供的规则组装对象,返回调用者。这便是SqlSession执行的过程。
参考:
https://blog.csdn.net/lom9357bye/article/details/80210661 ——【Mybatis源码】SqlSession四大对象
https://www.cnblogs.com/shamo89/p/9957291.html ——Mybatis(四):MyBatis核心组件介绍原理解析和源码解读
https://blog.csdn.net/liming363873117/article/details/60467107 ——mybatis框架(二)——核心组件及其作用
《JavaEE 互联网轻量级框架整合开发》SSM框架和Redis实现