本文源代码来源于mybatis-spring-boot-starter的2.1.2版本
前面的章节陆续介绍了MapperPoxy的创建、MapperStatement的生成、Executor等核心组件,其实都是在为本文做铺垫。这篇我们详细介绍下Mybatis是怎么执行我们定义的sql语句的。篇幅较长,我先上张图。
时序图
环境准备
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.26</version>
</dependency>
编写Dao和对应Mapper:
public interface RoleMapper {
@Select("select * from role")
@Results({
@Result(property = "roleId",column = "role_id"),
@Result(property = "roleName",column = "role_name")
})
List<Role> selectALl();
}
编写单元测试,后面我们就用该测试方法就行MybatisSql语句执行的探究。
@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisApplicationTests {
@Resource
private RoleMapper roleMapper ;
@Test
public void query2(){
List<Role> roles = roleMapper.selectALl();
}
}
MapperProxy
当我们把Mapper类交给Spring管理的时候,实际上Spring容器中存储的是Mapper的代理对象MapperProxy
。使用代理对象调用我们自己的接口中的方法时,会执行InvocationHandler
实现类的 invoke() 方法。无疑,MapperProxy的**invoke()**方法一定是Mybatis执行SQL的入口,一起来看下:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
cachedInvoker(method).invoke(proxy, method, args, sqlSession)
这里我们看到最终的查询结果实际上cachedInvoker(method)
执行了invoke方法得到的。很明显cachedInvoker(method)
也是一个代理对象,我们一起来看看它。
2.1 invoke
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
//映射代理对象保存我们的查询方法缓存,如果第二次相同的方法,就会直接返回.
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
首先调用了methodCache.computeIfAbsent
这个方法,这个是映射代理对象保存我们的查询方法缓存,我们首次调用肯定是没有缓存的,就会进入new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()))
中。这里new了一个 MapperMethod
对象作为PlainMethodInvoker
构造参数,这个对象我们一会儿介绍,继续看。 PlainMethodInvoker
是MapperProxy的一个内部类:
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
}
PlainMethodInvoker
定义了成员属性mapperMethod
,并重写了invoke() 方法.所以绕了一圈最终执行的还是MapperMethod
的execute() 方法。我们来看看MapperMethod
是何方神圣。
MapperMethod
我们先从MapperMethod构造说起:
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
MapperMethod有两个属性command
和method
,看起来一个是和sql有关,一个是和本身的方法有关。
3.1 SqlCommand
SqlCommand我们之前说过,它会根据Mapper接口名和方法名去Configuration
中拿MappedStatement
。
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
//获取方法名
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
//获取MappedStatement 接口名+方法名
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
3.2 MethodSignature
MethodSignature是对方法的出入参做一些处理,比如根据方法返回类型设置标记,入参参数名等,放在 method
中,后面在ResultSetHandler
中会用到。
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
//解析方法返回类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
//返回值类型
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
//是否返回多调结果
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
//返回值是否是MAP
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
//resultHandler类型参数的位置
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
3.3 execute
//这个方法是对SqlSession的包装调用
public Object execute(SqlSession sqlSession, Object[] args) {
//定义返回结果
Object result;
//如果是INSERT操作
if (SqlCommandType.INSERT == command.getType()) {
//处理参数
Object param = method.convertArgsToSqlCommandParam(args);
//调用sqlSession的insert方法
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操作 那么情况会多一些 但是也都和sqlSession的查询方法一一对应
} else if (SqlCommandType.SELECT == command.getType()) {
//如果返回void 并且参数有resultHandler
//则调用 void select(String statement, Object parameter, ResultHandler handler);方法
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//如果返回多行结果这调用 <E> List<E> selectList(String statement, Object parameter);
//executeForMany这个方法调用的
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//如果返回类型是MAP 则调用executeForMap方法
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
//否则就是查询单个对象
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else if (SqlCommandType.FLUSH == command.getType()) {
result = sqlSession.flushStatements();
} else {
//如果全都不匹配 说明mapper中定义的方法不对
throw new BindingException("Unknown execution method for: " + command.getName());
}
//如果返回值为空 并且方法返回值类型是基础类型 并且不是VOID 则抛出异常
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;
}
根据sql语句的类型分别处理,对比看起来都是先获取param
,然后路由到sqlSession
的某个方法。因为我们的selectAll()在执行MethodSignature
方法时得到returnsMany
=true,所以我们跟进下
3.4 executeForMany
//如果参数含有rowBounds则调用分页的查询
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
//没有分页则调用普通查询
result = sqlSession.<E>selectList(command.getName(), param);
}
这里会继续 sqlSession
的selectList()方法:
SqlSession
4.1 SqlSessionTemplate
MapperFactoryBean在创建MapperFactory在getObject() 曾定义了SqlSession的类型—— SqlSessionTemplate
。
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
//==>
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
我们来看下SqlSessionTemplate
的selectList()方法
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
}
sqlSessionProxy
又是代理对象,老规矩继续看invoke方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取SqlSqlSession对象,如果没有特殊绑定就调用sqlSessionFactory的openSession创建
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
getSqlSession如果没有特殊配置会调用sessionFactory.openSession(executorType)
进行创建:
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);
//创建executor
final Executor executor = configuration.newExecutor(tx, execType);
//返回DefaultSqlSession
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();
}
}
- 返回的SqlSession实现类是
DefaultSqlSession
。 - ExecutorTy默认是SIMPLE,这里会实例化一个
CachingExecutor
委托给SimpleExecutor
执行具体的sql方法,前面的文章已经分析过了,这里不做介绍了。
4.2 DefaultSqlSession
我们继续来看DefaultSqlSession的selectList() 方法:
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//调用executor的query方法
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();
}
}
Executor
5.1 CachingExecutor
上面提到了,在创建SqlSession
的时候会实例化CachingExecutor
,来看下CachingExecutor
的query方法:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//获取缓存
Cache cache = ms.getCache();
if (cache != null) {
// 是否需要清除缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 委托给BaseExecutor的实现类继续执行
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//放入缓存
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 委托给BaseExecutor的实现类继续执行
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
CachingExecutor
在执行的时候,会先判断是否有缓存,没有的话执行完写入缓存。delegat
就是CachingExecutor
的委托对象,默认是SimpleExecutor
。
5.2 BaseExecutor
我们先来看下SimpleExecutor
的父亲BaseExecutor
的query:
@Override
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) {
//此处尝试对Callable类型的表达式进行处理,主要是针对mode=out类型的参数
//存储过程使用
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//从数据库中获取并进行缓存处理,其也会调用子类需复写的doQuery()方法
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
//如果一级缓存的范围是statement级别,则每次查询都清空一级缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
这里有关于mybatis缓存的使用,我们后面会详细讲解。如果一级缓存是statement级别的,每次使用后都会清除。我们直接来看走数据库queryFromDatabase() 方法。
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);
//存储过程 out参数也加入缓存 这里可以忽略
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
doQuery() 是抽象方法,这里实际上是执行SimpleExecutor
的doQuery() 方法。
5.3 SimpleExecutor
@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
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//创建Statement对象,注意,这里就是JDBC协议的java.sql.Statement对象了
stmt = prepareStatement(handler, ms.getStatementLog());
//执行SQL语句
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQuery主要三件事情:
- 使用
configuration.newStatementHandler
创建StatementHandler ,我们在上一章节已经提到,这里不过多讲解。 - 创建Statement(java.sql.Statement)对象。
- 使用Statement对象执行sql语句。
创建Statement对象
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;
}
6.1 获取java.sql.Connection对象
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
这个transaction
是Srping的事务管理器SpringManagedTransaction
.
@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
openConnection();
}
return this.connection;
}
继续来看openConnection():
private void openConnection() throws SQLException {
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
+ (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
}
dataSource就是我们配置在yml中的数据源,调用org.springframework.jdbc.datasource.DataSourceUtils
的doGetConnection() 获取。
6.2 创建java.sql.Statement对象
调用链路是:SimpleExecutor->RoutingStatementHandler->BaseStatementHandler
@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);
}
}
- 根据Connection对象来创建Statement对象,其默认实现类
PreparedStatementHandler
的instantiateStatement方法。 - 为Statement设置超时时间,statement的timeout不是整个查询的timeout,只是statement执行完成并拉取fetchSize数据返回的超时。
- 为Statement设置FetchSize,对于查询数据量大的场景下,非常有必要设置
fetchSize
,否则全量拉取很容易OOM。
继续看instantiateStatement() :
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
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() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
connection.prepareStatement是创建Statement对象的核心,connection是druid的连接,由druid来实现。DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder)
。
执行Sql
调用链路:SimpleExecutor->RoutingStatementHandler->PrepareStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//这里会调用druid的execute
ps.execute();
//执行结果
return resultSetHandler.handleResultSets(ps);
}
7.1 execute
因为我们的数据源连接池配置的是druid。这里execute会调用到druid的实现:
@Override
public boolean execute() throws SQLException {
checkOpen();
//增加sql执行次数
incrementExecuteCount();
//将sql保存在transactionInfo中
transactionRecord(sql);
// oracleSetRowPrefetch();
conn.beforeExecute();
try {
//执行jdbc的execute
return stmt.execute();
} catch (Throwable t) {
throw checkException(t);
} finally {
conn.afterExecute();
}
}
7.2 handleResultSets
7.2.1 映射结果入口
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);
//根据resultMap处理rsw生成java对象
handleResultSet(rsw, resultMap, multipleResults, null);
//获取结果集的下一个结果
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
//和resultMaps的遍历处理类似
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);
}
resultMap
是handleResultSets方法的核心,当 mybatis 初始化完成后上面的配置都放到 MappedStatement.resultMaps
里面,在解析的时候就是通过 resultMap.id 取到对应的 resultMap 然后逐次解析。 来看下handleResultSet(rsw, resultMap, null, parentMapping):
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
// 创建默认的结果处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理结果集的行数据
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 将结果加入multipleResults中
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
可以看到,虽然按照parentMapping和resultHandler分成了3种情况,但最终都进入了handleRowValues方法。来下看handleRowValues方法:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
//嵌套结构
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//处理简单映射,本文先只分析简单映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
本文只分析简单映射,关于嵌套结构的处理后面有时间会单独再写一篇分析。继续看handleRowValuesForSimpleResultMap:
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
// 根据 RowBounds 定位到指定行记录
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
//根据discriminate找到适合的ResultMap
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
//生成java对象,附带懒加载
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
7.2.2 创建实体对象
我们跟踪下getRowValue() 中的createResultObject
看下是怎么创建java对象的:
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>();
final List<Object> constructorArgs = new ArrayList<>();
//创建对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
/* 如果开启了延迟加载,则为 resultObject生成代理类,
如果仅仅是配置的关联查询,没有开启延迟加载,是不会创建代理类
* 创建代理类,默认使用 Javassist 框架生成代理类。
* 由于实体类通常不会实现接口,所以不能使用 JDK 动态代理 API 为实体类生成代理。
* 并且将lazyLoader传进去了
*/
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}
继续来看createResultObject :
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
//存在适用的typeHanlder类,事实上一般为基本数据类型或者其封装类
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
//有参构造函数的constructor映射
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
//接口或者无参构造函数
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
//有参构造函数的自动映射
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
7.2.3 结果集映射
我们回到getRowValue() 中,这里对是否自动映射分别做了处理,我们先来看处理自动映射的applyAutomaticMappings
,他的判断条件是shouldApplyAutomaticMappings
:
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
//resultMap配置了autoMapping = true
if (resultMap.getAutoMapping() != null) {
return resultMap.getAutoMapping();
} else {
//xml setting的属性autoMappingBehavior,有3个值:NONE(不启用),PARTIAL(不嵌套的时候启动),FULL(启动)
if (isNested) {
return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
} else {
return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
}
}
}
applyAutomaticMappings
:结果集中有的column,但resultMap中并没有配置。
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) {
// 通过 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')
// 通过元信息对象设置 value 到实体类对象的指定字段上
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
//autoMappingsCache作为缓存,首先从缓存中获取
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
//缓存未命中
if (autoMapping == null) {
autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,
// ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
//驼峰
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
//存在set方法
if (property != null && metaObject.hasSetter(property)) {
//resultMap的应映射中已存在,忽略
if (resultMap.getMappedProperties().contains(property)) {
continue;
}
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
//没找到,根据autoMappingUnknownColumnBehavior属性(默认为NONE)进行处理:NONE(忽略),WARNING(log.warn),ERROR(抛异常)
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {
// //没找到,根据autoMappingUnknownColumnBehavior属性(默认为NONE)进行处理:NONE(忽略),WARNING(log.warn),ERROR(抛异常)
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
//加入缓存
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
applyPropertyMappings
:根据<resultMap>
节点中配置的映射关系进行映射
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
//嵌套查询的属性,忽略column
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
//字段值
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
//赋值给对象
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
从 ResultMap
获取映射对象 ResultMapping
集合。然后遍历 ResultMapping
集合,再此过程中调用 getPropertyMappingValue 获取指定指定列的数据,最后将获取到的数据设置到实体类对象中。
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
// 获取关联查询结果
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping);
return DEFERED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 从 ResultSet 中获取指定列的值
return typeHandler.getResult(rs, column);
}
}
与handleResultSets相关的延时加载、关联查询、嵌套查询等知识点,由于篇幅有限,后面我们在开一篇文章介绍吧。