MyBatis原理解析之运行原理
Posted duo-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis原理解析之运行原理相关的知识,希望对你有一定的参考价值。
初衷
回顾以往的学习经历,面对知识发现自己大多是一扫而过,并未了解其中深意,学过的知识一知半解,决定好好的再学一遍。以前写的博客内容少而且不得要领,这次希望自己一步一个脚印,踏踏实实的学进去,理解并运用所学。干吧碟!
为啥要开始学习mybatis呢,之前在复习javaweb的时候学习到了反射、注解和代理的时候,跟着视频写一个简单的JPA框架,觉得和mybatis在原理上会有相通的地方,觉得趁着学习的兴趣来好好研究一下。
简单JPA框架
主要程序功能如下
- BaseDao
- 加载数据源,连接数据库,定制操作模板(add()-实现添加用户的功能)
- Table
- 自定义注解,为用户实例添加映射的表的名称
- TestDao
- 测试往数据库添加用户的功能
- User
- 用户模型
- UserDao
- 继承BaseDao,add()
整个框架比较简单,实现的功能就是通过注解的形式往数据库中添加用户。
主要的处理逻辑在BaseDao中的add()中。
add()逻辑如下:
- 通过注解获取表名拼接SQL语句;
- 通过反射获取加载类的字节码文件,获取各字段的值并添加到链表中 ;
- 将链表内容转化为对象数组传入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-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 }
经过上述操作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中有配置信息,根据配置信息来得到最后的返回结果。
经过以上分析,我们有了五个主要问题:
- 如何解析mybatis-config.xml?
- 如何利用sqlSessionFactory与数据库建立连接?
- 如何获取UserMapper实例?
- 如何解析UserMapper.xml?
- 如何处理返回结果?
接下来一节将围绕这五个问题来作具体分析。
原理解析
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 }
这里我们以<properties></properties>标签为例,再细致的看一下里面的内容如何解析?
1 <properties resource="db.properties"> 2 <property name="username" value="root"/> 3 <property name="password" value="root"/> 4 </properties>
与读取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 }
再看源码的过程中,发现了一个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 }
2、如何利用sqlSessionFactory与数据库建立连接?
通过调试发现,sqlSessionFactory中有一个Configuration,该配置暴保存了整个配置环境信息。
而获取sqlSession的逻辑是
public static SqlSession openSqlSession(){ return sqlSessionFactory.openSession(); }
进入源码内部主要逻辑为
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 }
其中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 }
在 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 }
根据执行的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 }
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 }
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 }
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 }
在 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 }
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 }
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 }
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 }
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 }
在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 }
其中会创建构造器实例,
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 }
处理完结果后,会关闭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 }
总结
以上过程并不是很完美,但是记录一下,希望以后的自己看到之后可以有不同的体验。
以上是关于MyBatis原理解析之运行原理的主要内容,如果未能解决你的问题,请参考以下文章