mybatis源码解析(三)-SqlSession.selectOne类似方法调用过程
2018-01-30 10:22
896 查看
mybatis源码解析(一)-开篇
mybatis源码解析(二)-加载过程
mybatis源码解析(三)-SqlSession.selectOne类似方法调用过程
mybatis源码解析(四)-Mapper方法调用过程
mybatis源码解析(五)-mybatis如何实现的事务控制
mybatis源码解析(六)-配合spring-tx实现事务的原理
mybatis源码解析(七)-当mybatis一级缓存遇上spring
转载请标明出处:
http://blog.csdn.net/bingospunky/article/details/79202721
本文出自马彬彬的博客
上篇博客中介绍了mybatis的加载过程,这篇博客介绍一下org.apache.ibatis.session.SqlSession的增、删、改、查方法是怎么实现的。我们使用mybatis时,基本都是使用Mapper进行增、删、改、查操作,但是SqlSession也给了我们方法来完成相似的功能。从源码的角度去看,Mapper调用了SqlSession,所以研究SqlSession是很有必要的,且是研究Mapper的基础。
Code 1
第4行,就从org.apache.ibatis.session.Configuration里的protected final Map
第2行就是通过MappedStatement还有查询参数来生成BoundSql。BoundSql就是对sql的包装,包含sql语句,参数,List(参数的mapping)。生成BoundSql的核心代码如下:
Code 3
生成BoundSql的过程是比较复杂的。第2行构造DynamicContext,并且把参数传递进去,第3行把sql传递进DynamicContext,DynamicContext中的sql语句是包含#{xxx}的。第6行生成SqlSource,SqlSource是个接口,只有一个方法BoundSql getBoundSql(Object var1);,所以SqlSource就是来生成BoundSql的,SqlSource里包含sql和private List parameterMappings;,这里的sql就把占位符换成了?,ParameterMapping的个数和占位符一样多,ParameterMapping这个类包含了传递进去的参数是如何解析的,就像我们写sql时,#{xxx}里面会加上jdbcType等属性。第7行通过sqlSource和传递进来的参数(Map)生成BoundSql,这一行的代码比较简单,直接new一个BoundSql就行,因为BoundSql需要的内容已经在上面几行都凑出来了。其余代码忽略。
再看Code 2,第3行是缓存相关的,我们先忽略掉缓存相关的,后面也会先忽略掉缓存。第4行执行sql的代码如下:
Code 4
第5、6行没有复杂逻辑,忽略掉;第7行是生成Statement;第8行是执行Statement,然后解析ResultSet生成对应的Bean。第7、8行的内容是很复杂的,有很多的细节,后面也只是记录主要的过程,第7行生成Statement的主要代码如下:
Code 5
获取Statement的过程分为两步:第一步通过sql(包含?的)创建PreparedStatement,代码第3行所完成的;第二部给这个Statement设置参数,代码第4行所完成的,设置参数的过程大体上就是遍历List parameterMappings,在传递进来的参数中获取到适当的参数,然后设置到Statement中。这儿可以关注一点,给Statement设置参数的时候需要根据不同的参数类型调用不同的setXXX方法,这是根据参数的Java类型和JdbcType找到合适的TypeHadler,然后使用TypeHadler给Statement参数赋值。关于TypeHadler具体描述可以参考后面内容。
Code 4中第8行执行Statement的代码就一句话,和解析结果的代码是分开的,这里不贴执行Statement的代码了,解析ResultSet的主要代码如下:
Code 6
第5行构造了ResultSetWrapper,ResultSetWrapper是对ResultSet的包装,生成了一些column的信息,有column的name、jdbcType、className。
第6行获取了List resultMaps,一般就一个,一般可以再xxxMapper.xml文件的resultMap配置。
第9行的循环次数是List resultMaps的个数,不是结果集的个数,这里不要搞混了。
第11行实现从ResultSet到Bean的转化,转化一个Bean的核心代码如下:
Code 7
第3行创建一个对象,属性都为空。
第10行给这个对象属性赋值,赋值的过程就是根据org.apache.ibatis.mapping.resultMap获List propertyMappings,遍历List propertyMappings,在ResultSet里获取相应的属性,获取时使用的是TypeHandler获取的,ResultMapping可以获取到TypeHandler,然后调用Bean适当的方法设置值,setXXX是使用反射调用的。
外层代码循环处理ResultSet,对于生成的每个Bean收集到org.apache.ibatis.executor.result.DefaultResultHandler。DefaultResultHandler中的list就包含了结果集中生成的所有Bean,这个list又被外层的List包含,外层的List对应的是不同的ResultMap解析出来的Bean集合,一般配置一个ResultMap,所以外层List就一个元素,至于什么情况下配置两个ResultMap我还没有见到。
至此,使用org.apache.ibatis.session.selectOne查询的过程就已经描述完了。
该方法的执行过程与上述org.apache.ibatis.session.SqlSession.selectOne的过程基本一致,他们执行到PreparedStatement.execute基本都是一样的,只是selectOne方法后面是解析ResultSet,而update方法是获取变更的记录数。
org.apache.ibatis.type.TypeHandlerRegistry把TypeHandler存放在一个两层的Map中,外层Map的key为Java Class,value为一个内层Map;内层Map的key为org.apache.ibatis.type.JdbcType(这是个枚举类型),value为对应的Handler。
mybatis源码解析(二)-加载过程
mybatis源码解析(三)-SqlSession.selectOne类似方法调用过程
mybatis源码解析(四)-Mapper方法调用过程
mybatis源码解析(五)-mybatis如何实现的事务控制
mybatis源码解析(六)-配合spring-tx实现事务的原理
mybatis源码解析(七)-当mybatis一级缓存遇上spring
转载请标明出处:
http://blog.csdn.net/bingospunky/article/details/79202721
本文出自马彬彬的博客
上篇博客中介绍了mybatis的加载过程,这篇博客介绍一下org.apache.ibatis.session.SqlSession的增、删、改、查方法是怎么实现的。我们使用mybatis时,基本都是使用Mapper进行增、删、改、查操作,但是SqlSession也给了我们方法来完成相似的功能。从源码的角度去看,Mapper调用了SqlSession,所以研究SqlSession是很有必要的,且是研究Mapper的基础。
org.apache.ibatis.session.SqlSession.selectOne方法
使用org.apache.ibatis.session.SqlSession.selectOne类似方法时,主要过程如下面代码:Code 1
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { List var5; try { MappedStatement ms = this.configuration.getMappedStatement(statement); var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception var9) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); } finally { ErrorContext.instance().reset(); } return var5; }
第4行,就从org.apache.ibatis.session.Configuration里的protected final Map
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql); return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
第2行就是通过MappedStatement还有查询参数来生成BoundSql。BoundSql就是对sql的包装,包含sql语句,参数,List(参数的mapping)。生成BoundSql的核心代码如下:
Code 3
public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(this.configuration, parameterObject); this.rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(this.configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); Iterator i$ = context.getBindings().entrySet().iterator(); while(i$.hasNext()) { Entry<String, Object> entry = (Entry)i$.next(); boundSql.setAdditionalParameter((String)entry.getKey(), entry.getValue()); } return boundSql; }
生成BoundSql的过程是比较复杂的。第2行构造DynamicContext,并且把参数传递进去,第3行把sql传递进DynamicContext,DynamicContext中的sql语句是包含#{xxx}的。第6行生成SqlSource,SqlSource是个接口,只有一个方法BoundSql getBoundSql(Object var1);,所以SqlSource就是来生成BoundSql的,SqlSource里包含sql和private List parameterMappings;,这里的sql就把占位符换成了?,ParameterMapping的个数和占位符一样多,ParameterMapping这个类包含了传递进去的参数是如何解析的,就像我们写sql时,#{xxx}里面会加上jdbcType等属性。第7行通过sqlSource和传递进来的参数(Map)生成BoundSql,这一行的代码比较简单,直接new一个BoundSql就行,因为BoundSql需要的内容已经在上面几行都凑出来了。其余代码忽略。
再看Code 2,第3行是缓存相关的,我们先忽略掉缓存相关的,后面也会先忽略掉缓存。第4行执行sql的代码如下:
Code 4
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; List var9; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = this.prepareStatement(handler, ms.getStatementLog()); var9 = handler.query(stmt, resultHandler); } finally { this.closeStatement(stmt); } return var9; }
第5、6行没有复杂逻辑,忽略掉;第7行是生成Statement;第8行是执行Statement,然后解析ResultSet生成对应的Bean。第7、8行的内容是很复杂的,有很多的细节,后面也只是记录主要的过程,第7行生成Statement的主要代码如下:
Code 5
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Connection connection = this.getConnection(statementLog); Statement stmt = handler.prepare(connection); handler.parameterize(stmt); return stmt; }
获取Statement的过程分为两步:第一步通过sql(包含?的)创建PreparedStatement,代码第3行所完成的;第二部给这个Statement设置参数,代码第4行所完成的,设置参数的过程大体上就是遍历List parameterMappings,在传递进来的参数中获取到适当的参数,然后设置到Statement中。这儿可以关注一点,给Statement设置参数的时候需要根据不同的参数类型调用不同的setXXX方法,这是根据参数的Java类型和JdbcType找到合适的TypeHadler,然后使用TypeHadler给Statement参数赋值。关于TypeHadler具体描述可以参考后面内容。
Code 4中第8行执行Statement的代码就一句话,和解析结果的代码是分开的,这里不贴执行Statement的代码了,解析ResultSet的主要代码如下:
Code 6
public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId()); List<Object> multipleResults = new ArrayList(); int resultSetCount = 0; ResultSetWrapper rsw = this.getFirstResultSet(stmt); List<ResultMap> resultMaps = this.mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); this.validateResultMapsCount(rsw, resultMapCount); while(rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount); this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null); rsw = this.getNextResultSet(stmt); this.cleanUpAfterHandlingResultSet(); ++resultSetCount; } String[] resultSets = this.mappedStatement.getResulSets(); if (resultSets != null) { while(rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId); this.handleResultSet(rsw, resultMap, (List)null, parentMapping); } rsw = this.getNextResultSet(stmt); this.cleanUpAfterHandlingResultSet(); ++resultSetCount; } } return this.collapseSingleResultList(multipleResults); }
第5行构造了ResultSetWrapper,ResultSetWrapper是对ResultSet的包装,生成了一些column的信息,有column的name、jdbcType、className。
第6行获取了List resultMaps,一般就一个,一般可以再xxxMapper.xml文件的resultMap配置。
第9行的循环次数是List resultMaps的个数,不是结果集的个数,这里不要搞混了。
第11行实现从ResultSet到Bean的转化,转化一个Bean的核心代码如下:
Code 7
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { ResultLoaderMap lazyLoader = new ResultLoaderMap(); Object resultObject = this.createResultObject(rsw, resultMap, lazyLoader, (String)null); if (resultObject != null && !this.typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { MetaObject metaObject = this.configuration.newMetaObject(resultObject); boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty(); if (this.shouldApplyAutomaticMappings(resultMap, false)) { foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, (String)null) || foundValues; } foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, (String)null) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; resultObject = foundValues ? resultObject : null; return resultObject; } else { return resultObject; } }
第3行创建一个对象,属性都为空。
第10行给这个对象属性赋值,赋值的过程就是根据org.apache.ibatis.mapping.resultMap获List propertyMappings,遍历List propertyMappings,在ResultSet里获取相应的属性,获取时使用的是TypeHandler获取的,ResultMapping可以获取到TypeHandler,然后调用Bean适当的方法设置值,setXXX是使用反射调用的。
外层代码循环处理ResultSet,对于生成的每个Bean收集到org.apache.ibatis.executor.result.DefaultResultHandler。DefaultResultHandler中的list就包含了结果集中生成的所有Bean,这个list又被外层的List包含,外层的List对应的是不同的ResultMap解析出来的Bean集合,一般配置一个ResultMap,所以外层List就一个元素,至于什么情况下配置两个ResultMap我还没有见到。
至此,使用org.apache.ibatis.session.selectOne查询的过程就已经描述完了。
org.apache.ibatis.session.SqlSession.update方法
该方法也是在SqlSession上直接执行的,不需要Mapper。这个方法执行增、删、改的sql。该方法的执行过程与上述org.apache.ibatis.session.SqlSession.selectOne的过程基本一致,他们执行到PreparedStatement.execute基本都是一样的,只是selectOne方法后面是解析ResultSet,而update方法是获取变更的记录数。
org.apache.ibatis.session.SqlSession参数
org.apache.ibatis.session.SqlSession系列方法,传递参数的话只能传递一个值,这个值可以是:单个对象、集合类型(Collection、Array)、Map。那么框架是如何支持他们的呢?原因是:框架包含org.apache.ibatis.reflection.MetaObject类和org.apache.ibatis.reflection.wrapper包下的一些XXXWrapper完成的。关于TypeHandler
org.apache.ibatis.type.TypeHandlerRegistry这个类里注册是很多个org.apache.ibatis.type.TypeHandler,TypeHandler的功能就是:在执行sql时把参数以适当类型设置给PreparedStatement,在解析ResultSet时把执行的列转化为对应的类型。org.apache.ibatis.type.TypeHandlerRegistry把TypeHandler存放在一个两层的Map中,外层Map的key为Java Class,value为一个内层Map;内层Map的key为org.apache.ibatis.type.JdbcType(这是个枚举类型),value为对应的Handler。
相关文章推荐
- mybatis源码解析(四)-Mapper方法调用过程
- Mybatis 源码分析一、 SqlSessionFactory的创建过程
- MyBatis源码分析——SqlSessionFactory实例的产生过程
- mybatis源码解析(二)生成SqlSessionFactory
- MyBatis 源码解析:通过源码深入理解 SQL 的执行过程
- mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建
- Mybatis SqlSessionTemplate 源码解析
- MyBatis 源码解析:通过源码深入理解 SQL 的执行过程
- Mybatis源码解析-Mapper执行SQL过程
- Mybatis SqlSessionTemplate 源码解析
- Mybatis-Spring SqlSessionTemplate 源码解析
- mybatis源码分析——SqlSessionFactory实例的产生过程
- 搭建一个mybatis,出现session.selectOne方法错误
- mybatis源码分析(1)——SqlSessionFactory实例的产生过程
- (四)MyBatis源码解析之SqlSession
- MyBatis-3.4.2-源码分析18:XML解析之RoleMapper userMapper = sqlSession.getMapper(RoleMapper.class)
- mybatis_sql执行过程源码解析
- Mybatis SqlSessionTemplate 源码解析
- Mybatis SqlSessionTemplate 源码解析
- 从源码角度解析android APP启动过程中各类及其方法的调用