MyBatis源码分析-SQL语句执行的详细流程_第1页
MyBatis源码分析-SQL语句执行的详细流程_第2页
MyBatis源码分析-SQL语句执行的详细流程_第3页
MyBatis源码分析-SQL语句执行的详细流程_第4页
MyBatis源码分析-SQL语句执行的详细流程_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

MyBatis源码分析-SQL语句执行的完整流程MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程。MyBatis框架主要完成的是以下2件事情:根据JDBC规范建立与数据库的连接。通过反射打通Java对象与数据库参数交互之间相互转换的关系。MyBatis框架是一种典型的交互式框架,先准备好交互的必要条件,然后构建一个交互的环境,在交互环境中划分会话,在会话中与数据库进行交互数据。1 MyBatis主要的类Configuration MyBatis所有的配置信息都维持在Configuration对象之中。SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数,ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换MappedStatement MappedStatement维护了一条节点的封装,SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回BoundSql 表示动态生成的SQL语句以及相应的参数信息以上几个类在SQL操作中都会涉及,在SQL操作中重点关注下SQL参数什么时候写入和结果集怎么转换为Java对象,这两个过程正好对应的类是PreparedStatementHandler和ResultSetHandler类。(图片来自深入理解mybatis原理 MyBatis的架构设计以及实例分析)2 SQL执行流程MyBatis主要设计目的还是为了让我们在执行SQL时对输入输出的数据的管理更加方便,所以方便的让我们写出SQL和方便的获取SQL的执行结果是MyBatis的核心竞争力。下面就用一个例子来从源码角度看一下SQL的完整执行流程。新建配置文件conf.xml: conf.xml首先建立数据表,这里就以user表为例 :复制代码DROP TABLE IF EXISTS user;CREATE TABLE user ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(32) NOT NULL, password VARCHAR(32) NOT NULL, sex int, email VARCHAR(32), phone VARCHAR(16), admin VARCHAR(16);复制代码然后新建与数据表对应的类User: User再新建usre表的配置文件: userMapper.xml最后新建测试类:复制代码/* * MyBatis测试类 */public class TestMain public static void main(String args) throws IOException String resouce = conf.xml; InputStream is = Resources.getResourceAsStream(resouce); / 构建sqlSession工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); / 获取sqlSession SqlSession session = sqlSessionFactory.openSession(); User user; try /* * 第一种方式: 直接执行已映射的 SQL 语句 */ String statement = com.luoxn28.dao.UserDao.getById; user = session.selectOne(statement, 1); System.out.println(user); finally session.close(); /* * 第二种方式: 执行更清晰和类型安全的代码 */ UserDao userDao = session.getMapper(UserDao.class);/ user = userDao.getById(1);/ System.out.println(user); 复制代码由于我们分析的是SQL的执行流程,那就重点关注下 user = session.selectOne(statement, 1); 这行代码 注意,传进去的参数是1。session是DefaultSqlSession类型的,因为sqlSessionFactory默认生成的SqlSession是DefaultSqlSession类型。selectOne()会调用selectList()。复制代码/ DefaultSqlSession类public List selectList(String statement, Object parameter, RowBounds rowBounds) try MappedStatement ms = configuration.getMappedStatement(statement); / CURD操作是交给Excetor去处理的 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); catch (Exception e) throw ExceptionFactory.wrapException(Error querying database. Cause: + e, e); finally ErrorContext.instance().reset(); 复制代码在DefaultSqlSession.selectList中的各种CURD操作都是通多Executor进行的,这里executor的类型是CachingExecutor,接着跳转到其中的query方法中。/ CachingExecutor 类public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException BoundSql boundSql = ms.getBoundSql(parameterObject); / 获取绑定的sql命令,比如SELECT * FROM xxx CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);getBoundSql为了获取绑定的sql命令,在创建完cacheKey之后,就进入到CachingExecutor 类中的另一个query方法中。复制代码/ CachingExecutor 类Overridepublic List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException Cache cache = ms.getCache(); if (cache != null) flushCacheIfRequired(ms); if (ms.isUseCache() & resultHandler = null) ensureNoOutParams(ms, parameterObject, boundSql); SuppressWarnings(unchecked) List list = (List) tcm.getObject(cache, key); if (list = null) list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); / issue #578 and #116 return list; return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);复制代码这里真正执行query操作的是SimplyExecutor代理来完成的,接着就进入到了SimplyExecutor的父类BaseExecutor的query方法中。复制代码/ SimplyExecutor的父类BaseExecutor类SuppressWarnings(unchecked)Overridepublic List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException ErrorContext.instance().resource(ms.getResource().activity(executing a query).object(ms.getId(); if (closed) throw new ExecutorException(Executor was closed.); if (queryStack = 0 & ms.isFlushCacheRequired() clearLocalCache(); List list; try queryStack+; /* * localCache是一级缓存,如果找不到就调用queryFromDatabase从数据库中查找 */ list = resultHandler = null ? (List) localCache.getObject(key) : null; if (list != null) handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); else list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); finally queryStack-; if (queryStack = 0) for (DeferredLoad deferredLoad : deferredLoads) deferredLoad.load(); / issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() = LocalCacheScope.STATEMENT) / issue #482 clearLocalCache(); return list;复制代码因为是第一次SQL查询操作,所以会调用queryFromDatabase方法来执行查询。复制代码/ SimplyExecutor的父类BaseExecutor类private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException List list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); finally localCache.removeObject(key); localCache.putObject(key, list); if (ms.getStatementType() = StatementType.CALLABLE) localOutputParameterCache.putObject(key, parameter); return list;复制代码从数据库中查询数据,进入到SimplyExecutor中进行操作。复制代码/ SimplyExecutor类public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException Statement stmt = null; try Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); / 子流程1:SQL查询参数的设置 stmt = prepareStatement(handler, ms.getStatementLog(); / StatementHandler封装了Statement / 子流程2:SQL查询操作和结果集的封装 return handler.query(stmt); finally closeStatement(stmt); 复制代码注意,在prepareStatement方法中会进行SQL查询参数的设置,也就是咱们最开始传递进来的参数,其值为1。handler.query(stmt)方法中会进行实际的SQL查询操作和结果集的封装(封装成Java对象)。当流程走到这里时,程序已经压栈有一定深度了,因为接下来程序分析会兵分两路,一方面深入到SQL查询及结果集的设置子流程1中,然后再深入到SQL查询操作和结果集的封装子流程2,因为还会回到这里,所以就来一张调用栈的特写吧:子流程1:SQL查询参数的设置复制代码/ SimplyExecutor类private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException Statement stmt; / 获取一个Connection Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout(); handler.parameterize(stmt); / 设置SQL查询中的参数值 return stmt;复制代码通过getConnection方法来获取一个Connection,调用prepare方法来获取一个Statement(这里的handler类型是RoutingStatementHandler,RoutingStatementHandler的prepare方法调用的是PrepareStatementHandler的prepare方法,因为PrepareStatementHandler并没有覆盖其父类的prepare方法,其实最后调用的是BaseStatementHandler中的prepare方法。是不是绕晕了,那就再看一遍吧 :) )。调用parameterize方法来设置SQL的参数值(这里最后调用的是PrepareStatementHandler中的parameterize方法,而PrepareStatementHandler.parameterize方法调用的是DefaultParameterHandler中的setParameters方法)。/ PrepareStatementHandler类Overridepublic void parameterize(Statement statement) throws SQLException parameterHandler.setParameters(PreparedStatement) statement);复制代码/ DefaultParameterHandler类Overridepublic void setParameters(PreparedStatement ps) ErrorContext.instance().activity(setting parameters).object(mappedStatement.getParameterMap().getId(); List parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) for (int i = 0; i parameterMappings.size(); i+) ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName) / issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); else if (parameterObject = null) value = null; else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass() value = parameterObject; else MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value = null & jdbcType = null) jdbcType = configuration.getJdbcTypeForNull(); try typeHandler.setParameter(ps, i + 1, value, jdbcType); catch (TypeException e) throw new TypeException(Could not set parameters for mapping: + parameterMapping + . Cause: + e, e); catch (SQLException e) throw new TypeException(Could not set parameters for mapping: + parameterMapping + . Cause: + e, e); 复制代码到这里为止,已经给Statement设置了最初传递进去的参数(值为1)了,那么接着分析流程2:流程2:SQL查询及结果集的设置/ RoutingStatementHandler类Overridepublic List query(Statement statement) throws SQLException return delegate.query(statement);复制代码/ RoutingStatementHandler类Overridepublic List query(Statement statement) throws SQLException / 这里就到了熟悉的PreparedStatement了 PreparedStatement ps = (PreparedStatement) statement; / 执行SQL查询操作 ps.execute(); / 结果交给ResultHandler来处理 return resultSetHandler. handleResultSets(ps);复制代码复制代码/ DefaultResultSetHandler类(封装返回值,将查询结果封装成Object对象)Overridepublic List handleResultSets(Statement stmt) throws SQLException ErrorContext.instance().activity(handling results).object(mappedStatement.getId(); final List multipleResults = new ArrayList(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null & resultMapCount resultSetCount) ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount+; String resultSets = mappedStatement.getResultSets(); if (resultSets != null) while (rsw != null & resultSetCount resultSets.length) ResultMapping parentMapping = nextResultMaps.get(resultSetsresultSetCount); if (parentMapping != null) String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount+; return collapseSingleResultList(multipleResults);复制代码ResultSetWrapper是ResultSet的包装类,调用getFirstResultSet方法获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等,这些信息都存储在ResultSetWrapper类中了。然后调用handleResultSet方法来来进行结果集的封装。复制代码/ DefaultResultSetHandler类private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults, ResultMapping parentMapping) throws SQLException try if (parentMapping != null) handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); else if (resultHandler = null) DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); multipleResults.add(defaultResultHandler.getResultList(); else handleRowValues(rsw, resultMap, resultHandler, rowBounds, null); finally / issue #228 (close resultsets) closeResultSet(rsw.getResultSet(); 复制代码这里调用handleRowValues方法来进行值的设置:复制代码/ DefaultResultSetHandler类public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException if (resultMap.hasNestedResultMaps() ensureNoRowBounds(); checkResultHandler(); handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); else / 封装数据 handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); 复制代码复制代码/ DefaultResultSetHandler类/ 封装数据private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException DefaultResultContext resultContext = new DefaultResultContext(); skipRows(rsw.getResultSet(), rowBounds); while (shouldProcessMoreRows(resultContext, rowBounds) & rsw.getResultSet().next() ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null); Object rowValue = getRowValue(rsw, discriminatedResultMap); storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet(); 复制代码复制代码/ DefaultResultSetHandler类private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException final ResultLoaderMap lazyLoader = new ResultLoaderMap(); / createResultObject为新创建的对象,数据表对应的类 Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null); if (resultObject != null & !hasTypeHandlerForResultObject(rsw, resultMap.getType() final MetaObject metaObject = configuration.newMetaObject(resultObject); boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty(); if (shouldApplyAutomaticMpings(resultMap, false) / 这里把数据填充进去,metaObject中包含了resultObject信息 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) | foundValues; foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) | foundValues; foundValues = lazyLoader.size() 0 | foundValues; resultObject = foundValues ? resultObject : null; return resultObject; return resultObject;复制代码复制代码/ DefaultResultSetHandler类(把ResultSet中查询结果填充到JavaBean中)private boolean apply

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论