MyBatis原理解析之运行原理

Posted duo-fighting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis原理解析之运行原理相关的知识,希望对你有一定的参考价值。

初衷

回顾以往的学习经历,面对知识发现自己大多是一扫而过,并未了解其中深意,学过的知识一知半解,决定好好的再学一遍。以前写的博客内容少而且不得要领,这次希望自己一步一个脚印,踏踏实实的学进去,理解并运用所学。干吧碟!

为啥要开始学习mybatis呢,之前在复习javaweb的时候学习到了反射、注解和代理的时候,跟着视频写一个简单的JPA框架,觉得和mybatis在原理上会有相通的地方,觉得趁着学习的兴趣来好好研究一下。

简单JPA框架

技术图片

 主要程序功能如下

  • BaseDao
    • 加载数据源,连接数据库,定制操作模板(add()-实现添加用户的功能)
  • Table
    • 自定义注解,为用户实例添加映射的表的名称
  • TestDao
    • 测试往数据库添加用户的功能
  • User
    • 用户模型
  • UserDao
    • 继承BaseDao,add()

整个框架比较简单,实现的功能就是通过注解的形式往数据库中添加用户。

技术图片


主要的处理逻辑在BaseDao中的add()中。

add()逻辑如下:

  1.  通过注解获取表名拼接SQL语句;
  2. 通过反射获取加载类的字节码文件,获取各字段的值并添加到链表中 ;
  3. 将链表内容转化为对象数组传入JdbcTemplate中的update()方法中执行操作;

 这种逻辑的缺点:

  • 首先需要拼接SQL语句,这在语句简单的时候还可以,在大量的语句操作下还是很难,还是希望用户能自己决定该写哪些语句;
  • 代码格式不规范,处理逻辑和SQL在同一个方法内,耦合度太高;

虽然逻辑简单,但是其中蕴含的原理是值得思考的。当我们往数据库中添加的用户的时候,首先我们需要新建用户信息,接下来就是获取这些用户信息后通过执行SQL语句更新信息。

我们希望获得的是:

  • 用户自定义语句,而不是拼接;
  • 代码解耦;

 Mybatis作为一款优秀的JPA框架,自然帮我们都解决了这些问题。

初识MyBatis

 程序运行

 简略代码如下

技术图片
 1 public interface UserMapper {
 2     List<User> getAllUser();
 3 }
 4 
 5 <?xml version="1.0" encoding="UTF-8" ?>
 6 <!DOCTYPE mapper
 7         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 8         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 9 
10 <mapper namespace="com.xuan.dao.UserMapper">
11     <select id="getAllUser" resultType="User" parameterType="int">
12         select * from test.user
13     </select>
14 </mapper>
15 
16 public class User {
17     private int id;
18     private String name;
19     private String pwd;
20     ......
21 }
22 public class MybatisUtils {
23 
24     private static SqlSessionFactory sqlSessionFactory;
25 
26     static {
27         String resource = "mybatis-config.xml"; 
28         InputStream inputStream = null;
29         try {
30             inputStream = Resources.getResourceAsStream(resource);
31         } catch (IOException e) {
32             e.printStackTrace();
33         }
34         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
35     }
36 
37     public static SqlSession openSqlSession(){
38         return sqlSessionFactory.openSession();
39     }
40 }
mybatis简单执行

 程序运行流程如下

技术图片

流程分析

要想从数据库获取用户信息,首先你得先连接上数据库吧,在第一步的mybatis-config.xml中的environment中有数据库连接信息,猜想在读取此配置文件时应该将连接信息读到去连接数据库了。而与此读取有关的代码是

技术图片
1 static {
2         String resource = "mybatis-config.xml"; 
3         InputStream inputStream = null;
4         inputStream = Resources.getResourceAsStream(resource);
5         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
6     }
View Code

 经过上述操作sqlSessionFactory中应该保存了mybatis-config.xml中的信息。而与获取SqlSession有关的sqlSessionFactory.openSession()应该是与数据库建立了会话,其中的逻辑应该是利用sqlSessionFactory保存的数据库连接信息与数据库建立了连接,此时已经有了执行SQL语句的条件。要执行SQL语句,就需要先获取到SQL语句,此时我们知道的信息有:UserMapper.xml注册到了mybatis-config.xml中,sqlSessionFactory中应该有映射器的信息。而在第二步时,SqlSession执行了getMapper()获取到了UserMapper实例。猜想是UserMapper是一个接口,UserMapper.xml绑定了UserMapper,在注册时相当于注册了UserMapper的信息,通过接口获取一个UserMapper的实例,内部可能用到了动态代理来获得实例。此时通过解析UserMapper.xml中的SQL语句,并将产生的实例与之绑定,此时我们就有了用户实例、SQL语句,还有已经打开的数据库连接,只需要执行就可以了。当然对于返回值的类型,在UserMapper.xml中有配置信息,根据配置信息来得到最后的返回结果。

经过以上分析,我们有了五个主要问题:

  1. 如何解析mybatis-config.xml?
  2. 如何利用sqlSessionFactory与数据库建立连接?
  3. 如何获取UserMapper实例?
  4. 如何解析UserMapper.xml?
  5. 如何处理返回结果?

接下来一节将围绕这五个问题来作具体分析。

原理解析

1、如何解析mybatis-config.xml?

 技术图片

 对<Configuration></Configuration>标签下的节点进行解析的代码为

技术图片
 1 private void parseConfiguration(XNode root) {
 2         try {
 3             this.propertiesElement(root.evalNode("properties"));
 4             Properties settings = this.settingsAsProperties(root.evalNode("settings"));
 5             this.loadCustomVfs(settings);
 6             this.loadCustomLogImpl(settings);
 7             this.typeAliasesElement(root.evalNode("typeAliases"));
 8             this.pluginElement(root.evalNode("plugins"));
 9             this.objectFactoryElement(root.evalNode("objectFactory"));
10             this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
11             this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
12             this.settingsElement(settings);
13             this.environmentsElement(root.evalNode("environments"));
14             this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
15             this.typeHandlerElement(root.evalNode("typeHandlers"));
16             this.mapperElement(root.evalNode("mappers"));
17         } catch (Exception var3) {
18             throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
19         }
20     }
View Code

这里我们以<properties></properties>标签为例,再细致的看一下里面的内容如何解析?

技术图片
1 <properties resource="db.properties">
2         <property name="username" value="root"/>
3         <property name="password" value="root"/>
4 </properties>
View Code

与读取properties有关的代码为 this.propertiesElement(root.evalNode("properties")); ,方法的内部细节为

 1  private void propertiesElement(XNode context) throws Exception {
 2         if (context != null) {
 3             Properties defaults = context.getChildrenAsProperties();  //得到各<property />中的键值对数据,存放在Properties中
 4             String resource = context.getStringAttribute("resource");  
 5             String url = context.getStringAttribute("url");
         //resource或url只能有一种存在
6 if (resource != null && url != null) { 7 throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); 8 } 9 10 if (resource != null) { 11 defaults.putAll(Resources.getResourceAsProperties(resource)); 12 } else if (url != null) { 13 defaults.putAll(Resources.getUrlAsProperties(url)); 14 } 15        //获取已经存在的配置 16 Properties vars = this.configuration.getVariables(); 17 if (vars != null) { 18 defaults.putAll(vars); 19 } 20 21 this.parser.setVariables(defaults); 22 this.configuration.setVariables(defaults); 23 }

上述代码中

  • Properties本身是一个hashtable,存放键值对数据 class Properties extends Hashtable<Object,Object> 。
  • resource和url我理解是代表一种资源策略,二者只能有其中一种存在,对应的是<properties resource="db.properties"> 中的内容。
  •  Properties vars = this.configuration.getVariables(); 代表已经存在的配置,若是已经存在的话,会将<property/>中的配置覆盖掉,体现了<property/>在mybatis-config.xml中的优先级水平。
  • 最后在解析器和大的配置环境(configuration)中保存解析的配置数据。

展示一下getChildrenAsProperties()的方法来帮助理解:

技术图片
 1 public Properties getChildrenAsProperties() {
 2         Properties properties = new Properties();
 3         Iterator var2 = this.getChildren().iterator();
 4 
 5         while(var2.hasNext()) {
 6             XNode child = (XNode)var2.next();
 7             String name = child.getStringAttribute("name");
 8             String value = child.getStringAttribute("value");
 9             if (name != null && value != null) {
10                 properties.setProperty(name, value);
11             }
12         }
13 
14         return properties;
15     }
View Code

再看源码的过程中,发现了一个configuration,其中保存了很多的变量,猜想这应该是一个对应mybatis-config.xml文件的一个大的配置环境。属性拿出来感受一下哈,后面慢慢再来消化。

技术图片
 1 public class Configuration {
 2     protected Environment environment;
 3     protected boolean safeRowBoundsEnabled;
 4     protected boolean safeResultHandlerEnabled;
 5     protected boolean mapUnderscoreToCamelCase;
 6     protected boolean aggressiveLazyLoading;
 7     protected boolean multipleResultSetsEnabled;
 8     protected boolean useGeneratedKeys;
 9     protected boolean useColumnLabel;
10     protected boolean cacheEnabled;
11     protected boolean callSettersOnNulls;
12     protected boolean useActualParamName;
13     protected boolean returnInstanceForEmptyRow;
14     protected String logPrefix;
15     protected Class<? extends Log> logImpl;
16     protected Class<? extends VFS> vfsImpl;
17     protected LocalCacheScope localCacheScope;
18     protected JdbcType jdbcTypeForNull;
19     protected Set<String> lazyLoadTriggerMethods;
20     protected Integer defaultStatementTimeout;
21     protected Integer defaultFetchSize;
22     protected ResultSetType defaultResultSetType;
23     protected ExecutorType defaultExecutorType;
24     protected AutoMappingBehavior autoMappingBehavior;
25     protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;
26     protected Properties variables;
27     protected ReflectorFactory reflectorFactory;
28     protected ObjectFactory objectFactory;
29     protected ObjectWrapperFactory objectWrapperFactory;
30     protected boolean lazyLoadingEnabled;
31     protected ProxyFactory proxyFactory;
32     protected String databaseId;
33     protected Class<?> configurationFactory;
34     protected final MapperRegistry mapperRegistry;
35     protected final InterceptorChain interceptorChain;
36     protected final TypeHandlerRegistry typeHandlerRegistry;
37     protected final TypeAliasRegistry typeAliasRegistry;
38     protected final LanguageDriverRegistry languageRegistry;
39     protected final Map<String, MappedStatement> mappedStatements;
40     protected final Map<String, Cache> caches;
41     protected final Map<String, ResultMap> resultMaps;
42     protected final Map<String, ParameterMap> parameterMaps;
43     protected final Map<String, KeyGenerator> keyGenerators;
44     protected final Set<String> loadedResources;
45     protected final Map<String, XNode> sqlFragments;
46     protected final Collection<XMLStatementBuilder> incompleteStatements;
47     protected final Collection<CacheRefResolver> incompleteCacheRefs;
48     protected final Collection<ResultMapResolver> incompleteResultMaps;
49     protected final Collection<MethodResolver> incompleteMethods;
50     protected final Map<String, String> cacheRefMap;
51     ......
52 }
View Code

2、如何利用sqlSessionFactory与数据库建立连接?

 通过调试发现,sqlSessionFactory中有一个Configuration,该配置暴保存了整个配置环境信息。

技术图片

而获取sqlSession的逻辑是

技术图片
 public static SqlSession openSqlSession(){
        return sqlSessionFactory.openSession();
}
View Code

进入源码内部主要逻辑为

技术图片
 1 public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
 2         return this.openSessionFromDataSource(execType, (TransactionIsolationLevel)null, autoCommit);
 3 }
 4 
 5 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
 6         Transaction tx = null;
 7 
 8         DefaultSqlSession var8;
 9         try {
10             Environment environment = this.configuration.getEnvironment();
11             TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
12             tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
13             Executor executor = this.configuration.newExecutor(tx, execType);
14             var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
15         } catch (Exception var12) {
16             this.closeTransaction(tx);
17             throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
18         } finally {
19             ErrorContext.instance().reset();
20         }
21 
22         return var8;
23 }
View Code

 其中Environment对应的是mybatis-config.xml有关数据库的逻辑,保存了事务工厂和数据源。

1 public final class Environment {
2     private final String id;
3     private final TransactionFactory transactionFactory;
4     private final DataSource dataSource;
5     ......
6 }

 获取事务工厂的逻辑为

1 private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
2         return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());
3 }

 此时返回的TransactionFactory是空的JdbcTransaction,

技术图片

 实际建立JdbcTransaction的是 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); ,此时会获得一个事务实例。

1 public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
2         return new JdbcTransaction(ds, level, autoCommit);
3 }
 1 public class JdbcTransaction implements Transaction {
 2     private static final Log log = LogFactory.getLog(JdbcTransaction.class);
 3     protected Connection connection;
 4     protected DataSource dataSource;
 5     protected TransactionIsolationLevel level;
 6     protected boolean autoCommit;
 7 
 8     public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
 9         this.dataSource = ds;
10         this.level = desiredLevel;
11         this.autoCommit = desiredAutoCommit;
12     }
13     ......
14 }

 而在获取到excutor实例后,就建立一个sqlSession。

 1 public class DefaultSqlSession implements SqlSession {
 2     private final Configuration configuration;
 3     private final Executor executor;
 4     private final boolean autoCommit;
 5     private boolean dirty;
 6     private List<Cursor<?>> cursorList;
 7 
 8     public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
 9         this.configuration = configuration;
10         this.executor = executor;
11         this.dirty = false;
12         this.autoCommit = autoCommit;
13     }
14     ......
15 }

3、如何获取UserMapper实例?

在mybatis-config.xml中注册了映射器UserMapper.xml,而在UserMapper.xml指定了UserMapper接口。猜想内部是通过反射来创建UserMapper代理实例。下面来验证一下。

1 UserMapper mapper0 = sqlSession.getMapper(UserMapper.class);

 从配置中获取mapper

1 public <T> T getMapper(Class<T> type) {
2         return this.configuration.getMapper(type, this);
3 }

 在映射器中需要注册UserMapper.xml,所有的映射文件都需要注册在一个mapperRegistry中。mapperRegistry内部有一个mapper容器,是一个HashMap,用来保存注册的mapper.xml中指定的UserMapper接口工厂。

 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap(); 。

1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
2          return this.mapperRegistry.getMapper(type, sqlSession);
3 }
 1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 2         MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
 3         if (mapperProxyFactory == null) {
 4             throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 5         } else {
 6             try {
 7                 return mapperProxyFactory.newInstance(sqlSession);
 8             } catch (Exception var5) {
 9                 throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
10             }
11         }
12 }

根据得到的类型,来获取UserMapper工厂,然后通过代理来获取UserMapper实例。

1 public T newInstance(SqlSession sqlSession) {
2         MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
3         return this.newInstance(mapperProxy);
4 }
5 
6 protected T newInstance(MapperProxy<T> mapperProxy) {
7         return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
8 }

4、如何解析UserMapper.xml?

在执行 List<User> userList = mapper0.getAllUser(); 时,通过代理实例从数据库中获得了所要查询的信息。在其中应该会有从UserMapper.xml获取sql语句的处理逻辑。

可以看到通过调试直接进入MapperProxy中,

技术图片
 1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 2         try {
 3             if (Object.class.equals(method.getDeclaringClass())) {
 4                 return method.invoke(this, args);
 5             }
 6 
 7             if (method.isDefault()) {
 8                 if (privateLookupInMethod == null) {
 9                     return this.invokeDefaultMethodJava8(proxy, method, args);
10                 }
11 
12                 return this.invokeDefaultMethodJava9(proxy, method, args);
13             }
14         } catch (Throwable var5) {
15             throw ExceptionUtil.unwrapThrowable(var5);
16         }
17 
18         MapperMethod mapperMethod = this.cachedMapperMethod(method);
19         return mapperMethod.execute(this.sqlSession, args);
20     }
View Code

在 mapperMethod.execute(this.sqlSession, args) 中执行了getAllUser()方法。

技术图片
 1 public Object execute(SqlSession sqlSession, Object[] args) {
 2         Object result;
 3         Object param;
 4         switch(this.command.getType()) {
 5         case INSERT:
 6             param = this.method.convertArgsToSqlCommandParam(args);
 7             result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
 8             break;
 9         case UPDATE:
10             param = this.method.convertArgsToSqlCommandParam(args);
11             result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
12             break;
13         case DELETE:
14             param = this.method.convertArgsToSqlCommandParam(args);
15             result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
16             break;
17         case SELECT:
18             if (this.method.returnsVoid() && this.method.hasResultHandler()) {
19                 this.executeWithResultHandler(sqlSession, args);
20                 result = null;
21             } else if (this.method.returnsMany()) {
22                 result = this.executeForMany(sqlSession, args);
23             } else if (this.method.returnsMap()) {
24                 result = this.executeForMap(sqlSession, args);
25             } else if (this.method.returnsCursor()) {
26                 result = this.executeForCursor(sqlSession, args);
27             } else {
28                 param = this.method.convertArgsToSqlCommandParam(args);
29                 result = sqlSession.selectOne(this.command.getName(), param);
30                 if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
31                     result = Optional.ofNullable(result);
32                 }
33             }
34             break;
35         case FLUSH:
36             result = sqlSession.flushStatements();
37             break;
38         default:
39             throw new BindingException("Unknown execution method for: " + this.command.getName());
40         }
41 
42         if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
43             throw new BindingException("Mapper method ‘" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
44         } else {
45             return result;
46         }
47 }
View Code

 根据执行的select语句,进入相应的处理。

 1 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
 2         Object param = this.method.convertArgsToSqlCommandParam(args);
 3         List result;
 4         if (this.method.hasRowBounds()) {
 5             RowBounds rowBounds = this.method.extractRowBounds(args);
 6             result = sqlSession.selectList(this.command.getName(), param, rowBounds);
 7         } else {
 8             result = sqlSession.selectList(this.command.getName(), param);
 9         }
10 
11         if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
12             return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
13         } else {
14             return result;
15         }
16 }

 可以看出执行的是selectList()方法:

 1 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
 2         List var5;
 3         try {
 4             MappedStatement ms = this.configuration.getMappedStatement(statement);
 5             var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
 6         } catch (Exception var9) {
 7             throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
 8         } finally {
 9             ErrorContext.instance().reset();
10         }
11 
12         return var5;
13 }

  在其中,经调试进入静态SQL中,

1 public class StaticSqlSource implements SqlSource {
2     private final String sql;
3     private final List<ParameterMapping> parameterMappings;
4     private final Configuration configuration;
5     .....
6 }

 技术图片

 接着执行

1 public BoundSql getBoundSql(Object parameterObject) {
2         return new BoundSql(this.configuration, this.sql, this.parameterMappings, parameterObject);
3 }

技术图片

 在获取到MappedStatement后,执行query,

1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
2         BoundSql boundSql = ms.getBoundSql(parameterObject);
3         CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
4         return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
5 }

继续进入query,

 1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 2         Cache cache = ms.getCache();
 3         if (cache != null) {
 4             this.flushCacheIfRequired(ms);
 5             if (ms.isUseCache() && resultHandler == null) {
 6                 this.ensureNoOutParams(ms, boundSql);
 7                 List<E> list = (List)this.tcm.getObject(cache, key);
 8                 if (list == null) {
 9                     list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
10                     this.tcm.putObject(cache, key, list);
11                 }
12 
13                 return list;
14             }
15         }
16 
17         return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);  //进入
18 }
技术图片
 1     public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 2         ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
 3         if (this.closed) {
 4             throw new ExecutorException("Executor was closed.");
 5         } else {
 6             if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
 7                 this.clearLocalCache();
 8             }
 9 
10             List list;
11             try {
12                 ++this.queryStack;
13                 list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
14                 if (list != null) {
15                     this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
16                 } else {
17                     list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);   //进入
18                 }
19             } finally {
20                 --this.queryStack;
21             }
22 
23             if (this.queryStack == 0) {
24                 Iterator var8 = this.deferredLoads.iterator();
25 
26                 while(var8.hasNext()) {
27                     BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
28                     deferredLoad.load();
29                 }
30 
31                 this.deferredLoads.clear();
32                 if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
33                     this.clearLocalCache();
34                 }
35             }
36 
37             return list;
38         }
39 }
View Code
技术图片
 1 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 2         this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
 3 
 4         List list;
 5         try {
 6             list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
 7         } finally {
 8             this.localCache.removeObject(key);
 9         }
10 
11         this.localCache.putObject(key, list);
12         if (ms.getStatementType() == StatementType.CALLABLE) {
13             this.localOutputParameterCache.putObject(key, parameter);
14         }
15 
16         return list;
17 }
View Code
技术图片
 1 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
 2         Statement stmt = null;
 3 
 4         List var9;
 5         try {
 6             Configuration configuration = ms.getConfiguration();
 7             StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
 8             stmt = this.prepareStatement(handler, ms.getStatementLog());
 9             var9 = handler.query(stmt, resultHandler);
10         } finally {
11             this.closeStatement(stmt);
12         }
13 
14         return var9;
15 }
View Code
1 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
2         PreparedStatement ps = (PreparedStatement)statement;
3         ps.execute();   //执行查询语句
4         return this.resultSetHandler.handleResultSets(ps);   //处理返回结果
5 }

接下看下如何处理查询语句的,

技术图片
 1 public boolean execute() throws SQLException {
 2         try {
 3             synchronized(this.checkClosed().getConnectionMutex()) {
 4                 JdbcConnection locallyScopedConn = this.connection;
 5                 if (!this.doPingInstead && !this.checkReadOnlySafeStatement()) {
 6                     throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), "S1009", this.exceptionInterceptor);
 7                 } else {
 8                     ResultSetInternalMethods rs = null;
 9                     this.lastQueryIsOnDupKeyUpdate = false;
10                     if (this.retrieveGeneratedKeys) {
11                         this.lastQueryIsOnDupKeyUpdate = this.containsOnDuplicateKeyUpdateInSQL();
12                     }
13 
14                     this.batchedGeneratedKeys = null;
15                     this.resetCancelledState();
16                     this.implicitlyCloseAllOpenResults();
17                     this.clearWarnings();
18                     if (this.doPingInstead) {
19                         this.doPingInstead();
20                         return true;
21                     } else {
22                         this.setupStreamingTimeout(locallyScopedConn);
23                         Message sendPacket = ((PreparedQuery)this.query).fillSendPacket();
24                         String oldDb = null;
25                         if (!locallyScopedConn.getDatabase().equals(this.getCurrentDatabase())) {
26                             oldDb = locallyScopedConn.getDatabase();
27                             locallyScopedConn.setDatabase(this.getCurrentDatabase());
28                         }
29 
30                         CachedResultSetMetaData cachedMetadata = null;
31                         boolean cacheResultSetMetadata = (Boolean)locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue();
32                         if (cacheResultSetMetadata) {
33                             cachedMetadata = locallyScopedConn.getCachedMetaData(((PreparedQuery)this.query).getOriginalSql());
34                         }
35 
36                         locallyScopedConn.setSessionMaxRows(((PreparedQuery)this.query).getParseInfo().getFirstStmtChar() == ‘S‘ ? this.maxRows : -1);
37                         rs = this.executeInternal(this.maxRows, sendPacket, this.createStreamingResultSet(), ((PreparedQuery)this.query).getParseInfo().getFirstStmtChar() == ‘S‘, cachedMetadata, false);
38                         if (cachedMetadata != null) {
39                             locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery)this.query).getOriginalSql(), cachedMetadata, rs);
40                         } else if (rs.hasRows() && cacheResultSetMetadata) {
41                             locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery)this.query).getOriginalSql(), (CachedResultSetMetaData)null, rs);
42                         }
43 
44                         if (this.retrieveGeneratedKeys) {
45                             rs.setFirstCharOfQuery(((PreparedQuery)this.query).getParseInfo().getFirstStmtChar());
46                         }
47 
48                         if (oldDb != null) {
49                             locallyScopedConn.setDatabase(oldDb);
50                         }
51 
52                         if (rs != null) {
53                             this.lastInsertId = rs.getUpdateID();
54                             this.results = rs;
55                         }
56 
57                         return rs != null && rs.hasRows();
58                     }
59                 }
60             }
61         } catch (CJException var11) {
62             throw SQLExceptionsMapping.translateException(var11, this.getExceptionInterceptor());
63         }
64 }
View Code

在 Message sendPacket = ((PreparedQuery)this.query).fillSendPacket(); 这里填充发送的消息。之后再数据库中查询到结果之后,会接着处理查询的结果。

5、如何处理返回结果?

 处理结果的逻辑如下:

技术图片
 1 public List<Object> handleResultSets(Statement stmt) throws SQLException {
 2         ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
 3         List<Object> multipleResults = new ArrayList();
 4         int resultSetCount = 0;
 5         ResultSetWrapper rsw = this.getFirstResultSet(stmt);
 6         List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
 7         int resultMapCount = resultMaps.size();
 8         this.validateResultMapsCount(rsw, resultMapCount);
 9 
10         while(rsw != null && resultMapCount > resultSetCount) {
11             ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
12             this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
13             rsw = this.getNextResultSet(stmt);
14             this.cleanUpAfterHandlingResultSet();
15             ++resultSetCount;
16         }
17 
18         String[] resultSets = this.mappedStatement.getResultSets();
19         if (resultSets != null) {
20             while(rsw != null && resultSetCount < resultSets.length) {
21                 ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
22                 if (parentMapping != null) {
23                     String nestedResultMapId = parentMapping.getNestedResultMapId();
24                     ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
25                     this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
26                 }
27 
28                 rsw = this.getNextResultSet(stmt);
29                 this.cleanUpAfterHandlingResultSet();
30                 ++resultSetCount;
31             }
32         }
33 
34         return this.collapseSingleResultList(multipleResults);
35     }
View Code
 1 private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
 2         ResultSet rs = stmt.getResultSet();
 3 
 4         while(rs == null) {
 5             if (stmt.getMoreResults()) {
 6                 rs = stmt.getResultSet();
 7             } else if (stmt.getUpdateCount() == -1) {
 8                 break;
 9             }
10         }
11 
12         return rs != null ? new ResultSetWrapper(rs, this.configuration) : null;
13 }

在ResultSet中可以看到有4行数据,

技术图片

 接下里会有一系列对获得的数据库的结果进、进行处理的过程。我们想啊,当获取到结果后,他要返回查询到的对象这个过程是如何进行的呢,换句话说就是如何将数据和我们建立的pojo对应起来的呢?在处理过程中我们看到这样一个方法,

技术图片
 1 private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
 2         Constructor<?>[] constructors = resultType.getDeclaredConstructors();
 3         Constructor<?> defaultConstructor = this.findDefaultConstructor(constructors);
 4         if (defaultConstructor != null) {
 5             return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
 6         } else {
 7             Constructor[] var7 = constructors;
 8             int var8 = constructors.length;
 9 
10             for(int var9 = 0; var9 < var8; ++var9) {
11                 Constructor<?> constructor = var7[var9];
12                 if (this.allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
13                     return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);  //进入
14                 } 
15             }
16 
17             throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
18         }
19     }
View Code
技术图片
 1 private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
 2         Constructor<?>[] constructors = resultType.getDeclaredConstructors();
 3         Constructor<?> defaultConstructor = this.findDefaultConstructor(constructors);
 4         if (defaultConstructor != null) {
 5             return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
 6         } else {
 7             Constructor[] var7 = constructors;
 8             int var8 = constructors.length;
 9 
10             for(int var9 = 0; var9 < var8; ++var9) {
11                 Constructor<?> constructor = var7[var9];
12                 if (this.allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
13                     return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
14                 }
15             }
16 
17             throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
18         }
19 }
View Code

 

 1 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
 2         try {
 3             if (parentMapping != null) {
 4                 this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);
 5             } else if (this.resultHandler == null) {
 6                 DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);
 7                 this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);
 8                 multipleResults.add(defaultResultHandler.getResultList());
 9             } else {
10                 this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);
11             }
12         } finally {
13             this.closeResultSet(rsw.getResultSet());
14         }
15 }

 

 1 private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor) throws SQLException {
 2         boolean foundValues = false;
 3 
 4         for(int i = 0; i < constructor.getParameterTypes().length; ++i) {  //循环获取一个对象的每一列的数据
 5             Class<?> parameterType = constructor.getParameterTypes()[i];
 6             String columnName = (String)rsw.getColumnNames().get(i);
 7             TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
 8             Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
 9             constructorArgTypes.add(parameterType);
10             constructorArgs.add(value);
11             foundValues = value != null || foundValues;
12         }
13 
14         return foundValues ? this.objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
15 }

 

技术图片
 1 public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
 2         Class<?> classToCreate = this.resolveInterface(type);
 3         return this.instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
 4     }
 5 
 6     private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
 7         try {
 8             Constructor constructor;
 9             if (constructorArgTypes != null && constructorArgs != null) {
10                 constructor = type.getDeclaredConstructor((Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
11 
12                 try {
13                     return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
14                 } catch (IllegalAccessException var8) {
15                     if (Reflector.canControlMemberAccessible()) {
16                         constructor.setAccessible(true);
17                         return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
18                     } else {
19                         throw var8;
20                     }
21                 }
22             } else {
23                 constructor = type.getDeclaredConstructor();
24 
25                 try {
26                     return constructor.newInstance();
27                 } catch (IllegalAccessException var7) {
28                     if (Reflector.canControlMemberAccessible()) {
29                         constructor.setAccessible(true);
30                         return constructor.newInstance();
31                     } else {
32                         throw var7;
33                     }
34                 }
35             }
36         } catch (Exception var9) {
37             String argTypes = (String)((List)Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)).stream().map(Class::getSimpleName).collect(Collectors.joining(","));
38             String argValues = (String)((List)Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)).stream().map(String::valueOf).collect(Collectors.joining(","));
39             throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + var9, var9);
40         }
41 }
View Code

 

技术图片
 1 private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
 2         try {
 3             Constructor constructor;
 4             if (constructorArgTypes != null && constructorArgs != null) {
 5                 constructor = type.getDeclaredConstructor((Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
 6 
 7                 try {
 8                     return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
 9                 } catch (IllegalAccessException var8) {
10                     if (Reflector.canControlMemberAccessible()) {
11                         constructor.setAccessible(true);
12                         return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
13                     } else {
14                         throw var8;
15                     }
16                 }
17             } else {
18                 constructor = type.getDeclaredConstructor();
19 
20                 try {
21                     return constructor.newInstance();
22                 } catch (IllegalAccessException var7) {
23                     if (Reflector.canControlMemberAccessible()) {
24                         constructor.setAccessible(true);
25                         return constructor.newInstance();
26                     } else {
27                         throw var7;
28                     }
29                 }
30             }
31         } catch (Exception var9) {
32             String argTypes = (String)((List)Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)).stream().map(Class::getSimpleName).collect(Collectors.joining(","));
33             String argValues = (String)((List)Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)).stream().map(String::valueOf).collect(Collectors.joining(","));
34             throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + var9, var9);
35         }
36 }
View Code

 

在handleRowValues方法后会出现创建结果对象的逻辑

技术图片
 1 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
 2         ResultLoaderMap lazyLoader = new ResultLoaderMap();
 3         Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
 4         if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
 5             MetaObject metaObject = this.configuration.newMetaObject(rowValue);
 6             boolean foundValues = this.useConstructorMappings;
 7             if (this.shouldApplyAutomaticMappings(resultMap, false)) {
 8                 foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
 9             }
10 
11             foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
12             foundValues = lazyLoader.size() > 0 || foundValues;
13             rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
14         }
15 
16         return rowValue;
17 }
View Code

 

其中会创建构造器实例,

技术图片
 1 private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
 2         try {
 3             Constructor constructor;
 4             if (constructorArgTypes != null && constructorArgs != null) {
 5                 constructor = type.getDeclaredConstructor((Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
 6 
 7                 try {
 8                     return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
 9                 } catch (IllegalAccessException var8) {
10                     if (Reflector.canControlMemberAccessible()) {
11                         constructor.setAccessible(true);
12                         return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
13                     } else {
14                         throw var8;
15                     }
16                 }
17             } else {
18                 constructor = type.getDeclaredConstructor();
19 
20                 try {
21                     return constructor.newInstance();
22                 } catch (IllegalAccessException var7) {
23                     if (Reflector.canControlMemberAccessible()) {
24                         constructor.setAccessible(true);
25                         return constructor.newInstance();
26                     } else {
27                         throw var7;
28                     }
29                 }
30             }
31         } catch (Exception var9) {
32             String argTypes = (String)((List)Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)).stream().map(Class::getSimpleName).collect(Collectors.joining(","));
33             String argValues = (String)((List)Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)).stream().map(String::valueOf).collect(Collectors.joining(","));
34             throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + var9, var9);
35         }
36 }
View Code

 

 

 

 处理完结果后,会关闭JdbcConnection,

技术图片
 1 protected void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException {
 2         JdbcConnection locallyScopedConn = this.connection;
 3         if (locallyScopedConn != null && !this.isClosed) {
 4             if (!(Boolean)this.dontTrackOpenResources.getValue()) {
 5                 locallyScopedConn.unregisterStatement(this);
 6             }
 7 
 8             if (this.useUsageAdvisor && !calledExplicitly) {
 9                 this.session.getProfilerEventHandler().processEvent((byte)0, this.session, this, (Resultset)null, 0L, new Throwable(), Messages.getString("Statement.63"));
10             }
11 
12             if (closeOpenResults) {
13                 closeOpenResults = !this.holdResultsOpenOverClose && !(Boolean)this.dontTrackOpenResources.getValue();
14             }
15 
16             if (closeOpenResults) {
17                 if (this.results != null) {
18                     try {
19                         this.results.close();
20                     } catch (Exception var6) {
21                     }
22                 }
23 
24                 if (this.generatedKeysResults != null) {
25                     try {
26                         this.generatedKeysResults.close();
27                     } catch (Exception var5) {
28                     }
29                 }
30 
31                 this.closeAllOpenResults();
32             }
33 
34             this.isClosed = true;
35             this.closeQuery();
36             this.results = null;
37             this.generatedKeysResults = null;
38             this.connection = null;
39             this.session = null;
40             this.warningChain = null;
41             this.openResults = null;
42             this.batchedGeneratedKeys = null;
43             this.pingTarget = null;
44             this.resultSetFactory = null;
45         }
46 }
View Code

 

 总结

以上过程并不是很完美,但是记录一下,希望以后的自己看到之后可以有不同的体验。

 

 

 

 

以上是关于MyBatis原理解析之运行原理的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis之trim标签的作用原理解析

MyBatis之trim标签的作用原理解析

Mybatis的解析和运行原理

深入浅出MyBatis:MyBatis解析和运行原理

mybatis运行原理

Mybatis实现原理深入解析 (转载)