本文源代码来源于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(); } } 复制代码
当我们把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)
也是一个代理对象,我们一起来看看它。
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构造说起:
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有关,一个是和本身的方法有关。
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); } } } 复制代码
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); } 复制代码
//这个方法是对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,所以我们跟进下
//如果参数含有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()方法:
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(); } } 复制代码
DefaultSqlSession
。 CachingExecutor
委托给 SimpleExecutor
执行具体的sql方法,前面的文章已经分析过了,这里不做介绍了。 我们继续来看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(); } } 复制代码
上面提到了,在创建 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
。
我们先来看下 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() 方法。
@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 ,我们在上一章节已经提到,这里不过多讲解。 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; } 复制代码
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() 获取。
调用链路是: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); } } 复制代码
PreparedStatementHandler
的 instantiateStatement 方法。 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)
。
调用链路: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); } 复制代码
因为我们的数据源连接池配置的是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(); } } 复制代码
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); } } 复制代码
我们跟踪下 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); } 复制代码
我们回到 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相关的延时加载、关联查询、嵌套查询等知识点,由于篇幅有限,后面我们在开一篇文章介绍吧。