初看Mybatis 源码 (三) SQL是怎么执行的
前面说到Java动态代理,Mybatis通过这种方式实现了我们通过getMapper方式得到的Dao接口,可以直接通过接口的没有实现的方法来执行sql。
AuthUserDao mapper = session.getMapper(AuthUserDao.class);
getMapper方法到底做了什么。跟踪getMapper方法,进入到 MapperProxyFactory 类的 newInstance(SqlSession sqlSession) 方法。
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
看过上一篇的例子,我们知道。最后是通过Proxy.newProxyInstance 来实现切入sql的。他的实现在MapperProxy的 invoke 方法里面。看下invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
简单来说,在invoke里面做了如下事情:
1. 通过我们调用的方法,如本例中的 selectAuthUserByName ,接口名来组合成语句。本例中是 com.mybatis.dao.AuthUserDao.selectAuthUserByName 。其实使用过sqlSession的selectOne(String statmet)之类的语句都知道。这个可以唯一定位到我们在sql映射文件中配置的sql语句
2. 通过返回值类型,定位到的语句的类型。确定最后应该执行的方法。是执行查询、删除、添加、修改等等。
看下mybatis的是如何做的:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
可以看到的是,他是通过我们在sql配置文件中的语句类型来判断是执行哪种操作。然后通过返回类型来确定查询是查一条还是查多条记录。最后这种操作的方式,也是回归到了sqlSession中的CRUD的操作的方法。
下面看下,一条sql语句到底是怎么执行的?我们知道Mybatis其实是对JDBC的一个封装。假如我执行
session.update("com.mybatis.dao.AuthUserDao.updateAuthUserEmailByName", test@email.com);
语句,追踪下来,Executor、 BaseStatementHandler等等。在 SimpleExecutor 中有如下代码:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
1. 首先获取相关配置信息,这个在初始化时,从配置文件中解析而来
2. 新建了一个handler
3. 做了执行statement之前的准备工作。看看准备了些什么,跟踪代码,最后进入了DataSource类的doGetConnection方法,该方法做如下操作:
private Connection doGetConnection(Properties properties) throws SQLException {
initializeDriver();
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
Driver driverInstance = (Driver)driverType.newInstance();
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
原来是通过prepareStatement 来执行了 我们初始化jdbc的操作。Class.forName DriverManager.getConnection. 这两步是在这里面完成的。
4. 将执行sql的部分交给handler
继续跟踪handler 可以看到SimpleStatementHandler 中。如下执行这个update语句
public int update(Statement statement)
throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
这边就完成了statement的操作,整个过程就是我们Jdbc的过程。原来真的就是对JDBC的简单封装。
其实Mybatis的整个执行过程,理解起来分为如下几个过程:
1. 加载配置文件
2. 解析配置文件,从配置文件中解析出来 datasource、mapper文件、事务配置等等。将配置信息保存在对象内
3. 调用相关语句,执行sql。在执行的方法中分别完成JDBC的一系列操作。
初看Mybatis 源码 (三) SQL是怎么执行的的更多相关文章
- 初看Mybatis 源码 (二) Java动态代理类
先抛出一个问题,用过Mybatis的都知道,我们只需要定义一个Dao的接口,在里面写上一些CRUD相关操作,然后配置一下sql映射文件,就可以达到调用接口中的方法,然后执行sql语句的效果,为什么呢? ...
- 初看Mybatis 源码 (一)
Mybatis 的使用,首先需要构建一个SqlSessionFactory 实例.而该实例可以通过SqlSessionFactoryBuilder来创建. String resource = &quo ...
- MyBatis 源码篇-SQL 执行的流程
本章通过一个简单的例子,来了解 MyBatis 执行一条 SQL 语句的大致过程是怎样的. 案例代码如下所示: public class MybatisTest { @Test public void ...
- 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis 源码分析 - SQL 的执行过程
* 本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析 ...
- 精尽MyBatis源码分析 - SQL执行过程(四)之延迟加载
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis源码分析-SQL语句执行的完整流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- Spring mybatis源码篇章-sql mapper配置文件绑定mapper class类
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(二) 背景知识 MappedStatement是mybatis操作sql ...
- spring事务源码分析结合mybatis源码(三)
下面将结合mybatis源码来分析下,这种持久化框架是如何对connection使用,来达到spring事务的控制. 想要在把mybatis跟spring整合都需要这样一个jar包:mybatis-s ...
随机推荐
- Java - 自定义异常(尚学堂第六章异常机制作业计算平均数)
编写一个计算N个学生分数平均分的程序.程序应该提示用户输入N的值,如何必须输入所有N个学生分数.如果用户输入的分数是一个负数,则应该抛出一个异常并捕获,提示“分数必须是正数或者0”.并提示用户再次输入 ...
- Python取时间,日期的总结
import datetime from datetime import timedelta now = datetime.datetime.now() #今天 today = now #昨天 yes ...
- (转)浅谈 Linux 系统中的 SNMP Trap
原文:https://www.ibm.com/developerworks/cn/linux/l-cn-snmp/index.html 简介 本文讲解 SNMP Trap,在介绍 Trap 概念之前, ...
- centos 7编译安装php7
0.下载php源代码 http://www.php.net/releases/ 1.配置编译环境 yum install -y gcc gcc++ libxml2-devel openssl open ...
- selenium+Python(cookie处理)
cookie 处理本节重点: driver.get_cookies() 获得 cookie 信息 add_cookie(cookie_dict) 向 cookie 添加会话信息 delete_cook ...
- SQL Cookbook—字符串
1.遍历字符串2.计算字符在字符串中出现的次数3.从字符串中删除不需要的字符4.将字符和数字数据分离5.判别字符串是不是字母数字型的6.提取姓名的大写首字母缩写7.按字符串中的部分内容排序8.按字符串 ...
- poj 2954 Triangle
pick公式+gcd公式 #include<iostream> #include<map> #include<string> #include<cstring ...
- poj 3601 Tower of Hanoi
Tower of Hanoi Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 1853 Accepted: 635 De ...
- SVN命令行怎么用?--转百度知道
http://zhidao.baidu.com/link?url=uPWXURahp5KzdXbgrGTb9-r-abGaNC-J7dkhFkMhf062OJ1jeCM5wpBCgDR7bDg8uFr ...
- 在本地用命令行创建一个git仓库,并推送到远程
首先,进入的gitStore目录下(没有的话自己创建一个) 1.git init 在gitStore目录下 初始化一个git仓库 2.git add 复制一个文件到gitStore目录下,然后执行gi ...