MyBatis源码浅析
2015-12-01 09:31
375 查看
MyBatis源码浅析
posted on 2014-12-15 09:50
Tim-Tom
url: http://www.cnblogs.com/timlearn/p/4161567.html
什么是MyBatis
MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手工设置参数以及抽取结果集。MyBatis 使用简单的 XML 或注解来配置和映射基本体,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
MyBatis简单示例
虽然在使用MyBatis时一般都会使用XML文件,但是本文为了分析程序的简单性,简单的测试程序将不包含XML配置,该测试程序包含一个接口、一个启动类:
?
UserMapper是一个接口,我们在构建sqlSessionFactory时通过configuration.addMapper(UserMapper.class)把该接口注册进了sqlSessionFactory中。从上面的代码中我们可以看出,要使用MyBatis,我们应该经过以下步骤:1、创建sqlSessionFactory(一次性操作);2、用sqlSessionFactory对象构造sqlSession对象;3、调用sqlSession的相应方法;4、关闭sqlSession对象。
在main方法中,我们没有配置sql,也没有根据查询结果拼接对象,只需在调用sqlSession方法时传入一个命名空间以及方法参数参数即可,所有的操作都是面向对象的。在UserMapper接口中,我们定制了自己的sql,MyBatis把书写sql的权利给予了我们,方便我们进行sql优化及sql排错。
JDBC基础回顾
直接使用JDBC是很痛苦的,JDBC连接数据库包含以下几个基本步骤:1、注册驱动 ;2、建立连接(Connection);3、创建SQL语句(Statement);4、执行语句;5、处理执行结果(ResultSet);6、释放资源,示例代码如下:
?
可以看到与直接使用JDBC相比,MyBatis为我们简化了很多工作:
1、把创建连接相关工作抽象成一个sqlSessionFactory对象,一次创建多次使用;
2、把sql语句从业务层剥离,代码逻辑更加清晰,增加可维护性;
3、自动完成结果集处理,不需要我们编写重复代码。
但是,我们应该知道的是,框架虽然能够帮助我们简化工作,但是框架底层的代码肯定还是最基础的JDBC代码,因为这是Java平台连接数据库的通用方法,今天我将分析一下MyBatis源码,看看MyBatis是如何把这些基础代码封装成一个框架的。
MyBatis调用流程
我们最终调用的是sqlSession对象上的方法,所以我们先跟踪sqlSession的创建方法:sqlSessionFactory.openSession(),最终这个方法会调用到DefaultSqlSessionFactory的以下方法:
?
最终返回的对象是一个DefaultSqlSession对象,在调试模式下,我们看到autoCommit为false,executor为CachingExecutor类型,在CachingExecutor里面有属性delegate,其类型为simpleExecutor:
现在,我们跟进DefaultSqlSession的selectOne()方法,查看该方法的调用流程,selectOne()方法又会调用selectList()方法:
?
可以看到要得到查询结果,最终还是要调用executor上的query方法,这里的executor是CachingExecutor实例,跟进程序得到如下代码:
?
MyBatis框架首先生成了一个boundSql和CacheKey,在boundSql中包含有我们传入的sql语句:
生成boundSql和CacheKey后会调用一个重载函数,在重载函数中,我们会检测是否有缓存,这个缓存是MyBatis的二级缓存,我们没有配置,那么直接调用最后一句delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql),前面说过这个delagate其实就是simpleExecutor,跟进去查看一下:
?
关键代码是以下三行:
?
首先尝试从localCache中根据key得到List,这里的localCache是MyBatis的一级缓存,如果得不到则调用queryFromDatabase()从数据库中查询:
?
其中关键代码是调用doQuery()代码,SimpleExecutor的doQuery()方法如下:
?
调用了prepareStatement方法,该方法如下:
?
终于,我们看到熟悉的代码了,首先得到Connection,然后从Connection中得到Statement,同时在调试模式下我们看到,我们的sql语句已经被设置到stmt中了:
现在Statement对象有了,sql也设置进去了,就只差执行以及对象映射了,继续跟进代码,我们会跟踪到org.apache.ibatis.executor.statement.
PreparedStatementHandler类的executor方法:
?
在这里,调用了ps.execute()方法执行sql,接下来调用的resultSetHandler.<E> handleResultSets(ps)方法明显是对结果集进行封装,我就不继续跟进了。
MyBatis的数据库连接池
上面一部分介绍了MyBatis执行的整体流程,这一部分打算讨论一个具体话题:MyBatis的数据库连接池。
我们知道,每次连接数据库时都创建Connection是十分耗费性能的,所以我们在写JDBC代码时,一般都会使用数据库连接池,把用过的Connection不是直接关闭,而是放入数据库连接池中,方便下次复用,开源的数据库连接池有DBCP、C3P0等,MyBatis也实现了自己的数据库连接池,在这一节我将探索一下MyBatis实现的数据库连接池源码。
跟进上一节的getConnection()方法,我们最终会进入JdbcTransaction的getConnection()方法,getConnection()方法又会调用openConnection()方法,而openConnection()又将调用dataSource的getConnection()方法:
?
这里的dataSource是PooledDataSource类型,跟进查看源码如下:
?
可以看到,在这里我们返回的对象其实已经不是原生的Connection对象了,而是一个动态代理对象,是PooledConnection的一个属性,所有对对Connection对象的操作都将被PooledConnection拦截,我们可以查看PooledConnection的定义如下:
?
可以看到这个类暴露了很多接口检测Connection状态,例如连接是否有效,连接创建时间最近使用连接等:
这个类实现了InvocationHandler接口,最主要的一个方法如下:
?
可以看到,PooledConnection会拦截close方法,当客户端调用close()方法时,程序不会关闭Connection,而是会调用dataSource.pushConnection(this)方法,该方法的实现如下:
?
可以看到,首先会把Connection从活跃列表中删除,然后检测空闲列表的长度有没有达到最大长度(默认为5),若没有达到,把Connection放入空闲链表,否则关闭连接。这里的state是一个PoolState对象,该对象定义如下:
?
可以看到最终我们的Connection对象是放在ArrayList中的,该类还提供一些接口返回连接池基本信息。
好了,现在我们可以回去看看PooledDataSource的popConnection方法了:
?
可以看到获取Connection一共分以下几种情况:1、如果有空闲Connection,那么直接使用空闲Connection,否则2;2、如果活跃Connection没有达到活跃Connection的上限,那么创建一个新Connection并返回,否则3;3、如果达到活跃上限,且被检出的Connection检出时间过长,那么把该Connection置为失效,新创建一个Connection,否则4;4、等待空闲Connection。
至此,我们就把MyBatis的数据库连接池代码整理了一遍,其中有两个关键点:1、检出的Connection其实不是原生Connection,而是一个代理对象;2、存放Connection的容器是ArrayList,Connection的检出遵从先进先出原则。
MyBatis的缓存
这篇博客讲的很好,mark一下:http://www.cnblogs.com/fangjian0423/p/mybatis-cache.html
MyBatis的事务
首先回顾一下JDBC的事务知识。
JDBC可以操作Connection的setAutoCommit()方法,给它false参数,提示数据库启动事务,在下达一连串的SQL命令后,自行调用Connection的commit()方法,提示数据库确认(Commit)操作。如果中间发生错误,则调用rollback(),提示数据库撤销(ROLLBACK)所有执行。同时,如果仅想要撤回某个SQL执行点,则可以设置存储点(SAVEPOINT)。一个示范的事务流程如下:
?
在MyBatis调用流程一节就写过,在调试模式下,我们看到autoCommit为false,所以每个sqlSession其实都是一个事务,这也是为什么每次做删、改、查时都必须调用commit的原因。
MyBatis源码浅析
posted on 2014-12-15 09:50
Tim-Tom
url: http://www.cnblogs.com/timlearn/p/4161567.html
什么是MyBatis
MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手工设置参数以及抽取结果集。MyBatis 使用简单的 XML 或注解来配置和映射基本体,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
MyBatis简单示例
虽然在使用MyBatis时一般都会使用XML文件,但是本文为了分析程序的简单性,简单的测试程序将不包含XML配置,该测试程序包含一个接口、一个启动类:
?
在main方法中,我们没有配置sql,也没有根据查询结果拼接对象,只需在调用sqlSession方法时传入一个命名空间以及方法参数参数即可,所有的操作都是面向对象的。在UserMapper接口中,我们定制了自己的sql,MyBatis把书写sql的权利给予了我们,方便我们进行sql优化及sql排错。
JDBC基础回顾
直接使用JDBC是很痛苦的,JDBC连接数据库包含以下几个基本步骤:1、注册驱动 ;2、建立连接(Connection);3、创建SQL语句(Statement);4、执行语句;5、处理执行结果(ResultSet);6、释放资源,示例代码如下:
?
1、把创建连接相关工作抽象成一个sqlSessionFactory对象,一次创建多次使用;
2、把sql语句从业务层剥离,代码逻辑更加清晰,增加可维护性;
3、自动完成结果集处理,不需要我们编写重复代码。
但是,我们应该知道的是,框架虽然能够帮助我们简化工作,但是框架底层的代码肯定还是最基础的JDBC代码,因为这是Java平台连接数据库的通用方法,今天我将分析一下MyBatis源码,看看MyBatis是如何把这些基础代码封装成一个框架的。
MyBatis调用流程
我们最终调用的是sqlSession对象上的方法,所以我们先跟踪sqlSession的创建方法:sqlSessionFactory.openSession(),最终这个方法会调用到DefaultSqlSessionFactory的以下方法:
?
现在,我们跟进DefaultSqlSession的selectOne()方法,查看该方法的调用流程,selectOne()方法又会调用selectList()方法:
?
?
生成boundSql和CacheKey后会调用一个重载函数,在重载函数中,我们会检测是否有缓存,这个缓存是MyBatis的二级缓存,我们没有配置,那么直接调用最后一句delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql),前面说过这个delagate其实就是simpleExecutor,跟进去查看一下:
?
?
?
?
?
现在Statement对象有了,sql也设置进去了,就只差执行以及对象映射了,继续跟进代码,我们会跟踪到org.apache.ibatis.executor.statement.
PreparedStatementHandler类的executor方法:
?
MyBatis的数据库连接池
上面一部分介绍了MyBatis执行的整体流程,这一部分打算讨论一个具体话题:MyBatis的数据库连接池。
我们知道,每次连接数据库时都创建Connection是十分耗费性能的,所以我们在写JDBC代码时,一般都会使用数据库连接池,把用过的Connection不是直接关闭,而是放入数据库连接池中,方便下次复用,开源的数据库连接池有DBCP、C3P0等,MyBatis也实现了自己的数据库连接池,在这一节我将探索一下MyBatis实现的数据库连接池源码。
跟进上一节的getConnection()方法,我们最终会进入JdbcTransaction的getConnection()方法,getConnection()方法又会调用openConnection()方法,而openConnection()又将调用dataSource的getConnection()方法:
?
?
?
这个类实现了InvocationHandler接口,最主要的一个方法如下:
?
?
?
好了,现在我们可以回去看看PooledDataSource的popConnection方法了:
?
至此,我们就把MyBatis的数据库连接池代码整理了一遍,其中有两个关键点:1、检出的Connection其实不是原生Connection,而是一个代理对象;2、存放Connection的容器是ArrayList,Connection的检出遵从先进先出原则。
MyBatis的缓存
这篇博客讲的很好,mark一下:http://www.cnblogs.com/fangjian0423/p/mybatis-cache.html
MyBatis的事务
首先回顾一下JDBC的事务知识。
JDBC可以操作Connection的setAutoCommit()方法,给它false参数,提示数据库启动事务,在下达一连串的SQL命令后,自行调用Connection的commit()方法,提示数据库确认(Commit)操作。如果中间发生错误,则调用rollback(),提示数据库撤销(ROLLBACK)所有执行。同时,如果仅想要撤回某个SQL执行点,则可以设置存储点(SAVEPOINT)。一个示范的事务流程如下:
?
相关文章推荐
- Viewpager最后到最前循环滑动
- Libnfc
- 解决Swing控件setEnabled(false)时的字体颜色
- ibatis 开发手册
- iOS中设置tableview的选中时的文字颜色
- JSON使用(4)
- java程序错误类型及异常处理
- 深入JavaScript高级程序设计之对象、数组(栈方法,队列方法,重排序方法,迭代方法)
- [LeetCode]Longest Increasing Subsequence
- 详细解析Java中抽象类和接口的区别
- java的抽象类
- Hudson:一款持续构建工具
- jvm参数调优,及参数配置
- win7+vs2010的程序移植到win8+vs2013
- day6 python学习随笔。
- [APUE]再读之文件和目录
- Hbase 知识点记录总结(1)
- JSON语法(3)
- Java的接口及实例
- c++删除文件