ImageVerifierCode 换一换
格式:DOCX , 页数:21 ,大小:20.97KB ,
资源ID:26783022      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/26783022.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(MyBatis源码分析SQL语句执行的完整流程.docx)为本站会员(b****4)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

MyBatis源码分析SQL语句执行的完整流程.docx

1、MyBatis源码分析SQL语句执行的完整流程MyBatis源码分析-SQL语句执行的完整流程MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程。MyBatis框架主要完成的是以下2件事情:根据JDBC规范建

2、立与数据库的连接。通过反射打通Java对象与数据库参数交互之间相互转换的关系。MyBatis框架是一种典型的交互式框架,先准备好交互的必要条件,然后构建一个交互的环境,在交互环境中划分会话,在会话中与数据库进行交互数据。1 MyBatis主要的类Configuration MyBatis所有的配置信息都维持在Configuration对象之中。SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护StatementHandler 封

3、装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数,ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换MappedStatement MappedStatement维护了一条节点的封装,SqlSource 负责根据用户传递的parameterObject,动态地生成SQL

4、语句,将信息封装到BoundSql对象中,并返回BoundSql 表示动态生成的SQL语句以及相应的参数信息以上几个类在SQL操作中都会涉及,在SQL操作中重点关注下SQL参数什么时候写入和结果集怎么转换为Java对象,这两个过程正好对应的类是PreparedStatementHandler和ResultSetHandler类。(图片来自深入理解mybatis原理 MyBatis的架构设计以及实例分析)2 SQL执行流程MyBatis主要设计目的还是为了让我们在执行SQL时对输入输出的数据的管理更加方便,所以方便的让我们写出SQL和方便的获取SQL的执行结果是MyBatis的核心竞争力。下面就

5、用一个例子来从源码角度看一下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);复制代码然后新建与数据表对

6、应的类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 SqlSess

7、ionFactoryBuilder().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(); /* * 第二种方式:

8、 执行更清晰和类型安全的代码 */ 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类型。selec

9、tOne()会调用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_RESUL

10、T_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(Mapped

11、Statement 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,

12、 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 S

13、QLException 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, pa

14、rameterObject, 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方法中。复制代码

15、/ 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 que

16、ry).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)

17、: 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 #6

18、01 deferredLoads.clear(); if (configuration.getLocalCacheScope() = LocalCacheScope.STATEMENT) / issue #482 clearLocalCache(); return list;复制代码因为是第一次SQL查询操作,所以会调用queryFromDatabase方法来执行查询。复制代码/ SimplyExecutor的父类BaseExecutor类private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds

19、 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

20、(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) t

21、hrows 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(); / Stat

22、ementHandler封装了Statement / 子流程2:SQL查询操作和结果集的封装 return handler.query(stmt); finally closeStatement(stmt); 复制代码注意,在prepareStatement方法中会进行SQL查询参数的设置,也就是咱们最开始传递进来的参数,其值为1。handler.query(stmt)方法中会进行实际的SQL查询操作和结果集的封装(封装成Java对象)。当流程走到这里时,程序已经压栈有一定深度了,因为接下来程序分析会兵分两路,一方面深入到SQL查询及结果集的设置子流程1中,然后再深入到SQL查询操作和结果集的

23、封装子流程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.ge

24、tTimeout(); handler.parameterize(stmt); / 设置SQL查询中的参数值 return stmt;复制代码通过getConnection方法来获取一个Connection,调用prepare方法来获取一个Statement(这里的handler类型是RoutingStatementHandler,RoutingStatementHandler的prepare方法调用的是PrepareStatementHandler的prepare方法,因为PrepareStatementHandler并没有覆盖其父类的prepare方法,其实最后调用的是BaseStatem

25、entHandler中的prepare方法。是不是绕晕了,那就再看一遍吧 :) )。调用parameterize方法来设置SQL的参数值(这里最后调用的是PrepareStatementHandler中的parameterize方法,而PrepareStatementHandler.parameterize方法调用的是DefaultParameterHandler中的setParameters方法)。/ PrepareStatementHandler类Overridepublic void parameterize(Statement statement) throws SQLExceptio

26、n parameterHandler.setParameters(PreparedStatement) statement);复制代码/ DefaultParameterHandler类Overridepublic void setParameters(PreparedStatement ps) ErrorContext.instance().activity(setting parameters).object(mappedStatement.getParameterMap().getId(); List parameterMappings = boundSql.getParameterMa

27、ppings(); 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.hasAdditionalPa

28、rameter(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 = config

29、uration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value = null & jdbcType = null) jdbcType = configuration.getJdbcTypeForNull(); try typeHandler.setPa

30、rameter(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查询及结果集的设置/ RoutingStat

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1