MyBatis源码解读(4)——SqlSession(上)
在上一篇博客中提到MyBatis是如何实现代理类MapperProxy,并抛出了一个问题——是怎么执行一个具体的sql语句的,在文末中提到了MapperMethod的execute采用命令模式来判断是何种sql语句,并将具体语句的执行交由SqlSession处理。所以此篇博客正是要讲到SqlSession。
在SqlSession接口中包含了所有可能执行的sql语句在这里不一一列举,请参考org.apache.ibatis.session.SqlSession源码。DefaultSqlSession是SqlSession的实现类,所以我们重点关注DefaultSqlSession类。在上一篇博客中提到了我们将选择MapperProxy的executeForMany方法。在此方法中调用了SqlSession.<E>selectList方法。所以我们来看看org.apache.ibatis.session.default.DefaultSqlSession类的selectList方法。
//org.apache.ibatis.session.defaults.DefaultSqlSession
……
@Override
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
} @Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
} @Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}
……
我们看到关于selectList有三个重载方法,最后调用的都是public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)。在此方法中第一个参数为String类型且命名为statement,第二个参数为Object命名为parameter,回到MapperMethod。
//org.apache.ibatis.binding.MapperMethod
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds); //注意传递了什么参数进来
} else {
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
第7行代码可以看到传递了什么参数进来,跟踪command.getName()是怎么来的。
private final SqlCommand command;
看来是一个叫做SqlCommand的变量,进而我们可以发现这个SqlCommand是MapperMethod的静态内部类。
//org.apache.ibatis.binding.MapperMethod
public static class SqlCommand { private final String name;
private final SqlCommandType type; public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
String statementName = mapperInterface.getName() + "." + method.getName(); //获取接口+方法名
MappedStatement ms = null; //定义一个MappedStatement,这个MapperedStatement稍后介绍
if (configuration.hasStatement(statementName)) { //从Configuration对象查找是否有这个方法的全限定名称
ms = configuration.getMappedStatement(statementName); //有,则根据方法的全限定名称获取MappedStatement实例
} else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35//如果没有在Configuration对象中找到这个方法,则向上父类中获取全限定方法名
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) { //在向上的父类查找是否有这个全限定方法名
ms = configuration.getMappedStatement(parentStatementName); //有,则根据方法的全限定名称获取MappedStatement实例
}
}
if (ms == null) {
if(method.getAnnotation(Flush.class) != null){
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): " + statementName);
}
} else {
name = ms.getId(); //这个ms.getId,其实就是我们在mapper.xml配置文件中配置一条sql语句时开头的<select id="……"……,即是接口的该方法名全限定名称
type = ms.getSqlCommandType(); //显然这是将sql是何种类型(insert、update、delete、select)赋给type
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
} public String getName() {
return name;
} public SqlCommandType getType() {
return type;
}
}
大致对MapperMethod的解读到此,再次回到DefaultSqlSession中,到这里可能稍微有点混乱。画个图来理解这一段,希望能帮助理解。
这次又一次回到了DefaultSqlSession.selectList。此处不再贴出selectList所有方法,而是只贴出一句,根据接口方法的全限定名称获取MappedStatement。
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
在获取到MappedStatement后,碰到了第一个SqlSession下的四大对象之一:Executor执行器。看来还是没有讲到SqlSession,再放到下一节吧。可以思考下这个MappedStatement是什么意思,望文生义就能猜出个大概。
这是一个能给程序员加buff的公众号
MyBatis源码解读(4)——SqlSession(上)的更多相关文章
- MyBatis源码解读(3)——MapperMethod
在前面两篇的MyBatis源码解读中,我们一路跟踪到了MapperProxy,知道了尽管是使用了动态代理技术使得我们能直接使用接口方法.为巩固加深动态代理,我们不妨再来回忆一遍何为动态代理. 我相信在 ...
- spring IOC DI AOP MVC 事务, mybatis 源码解读
demo https://gitee.com/easybao/aop.git spring DI运行时序 AbstractApplicationContext类的 refresh()方法 1: pre ...
- Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?
Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? 如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...
- Mybatis源码解读-SpringBoot中配置加载和Mapper的生成
本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...
- Mybatis源码解读-插件
插件允许对Mybatis的四大对象(Executor.ParameterHandler.ResultSetHandler.StatementHandler)进行拦截 问题 Mybatis插件的注册顺序 ...
- MyBatis源码解读之延迟加载
1. 目的 本文主要解读MyBatis 延迟加载实现原理 2. 延迟加载如何使用 Setting 参数配置 设置参数 描述 有效值 默认值 lazyLoadingEnabled 延迟加载的全局开关.当 ...
- MyBatis源码解读(1)——SqlSessionFactory
在前面对MyBatis稍微有点了解过后,现在来对MyBatis的源码试着解读一下,并不是解析,暂时定为解读.所有对MyBatis解读均是基于MyBatis-3.4.1,官网中文文档:http://ww ...
- 【转】Mybatis源码解读-设计模式总结
原文:http://www.crazyant.net/2022.html?jqbmtw=b90da1&gsjulo=kpzaa1 虽然我们都知道有26个设计模式,但是大多停留在概念层面,真实开 ...
- Mybatis源码分析之SqlSession和Excutor(二)
通过上一篇文章的分析我们,我初步了解了它是如何创建sessionFactory的(地址:Mybatis源码分析之SqlSessionFactory(一)), 今天我们分析下Mybatis如何创建Sql ...
- Mybatis源码解读-设计模式总结
虽然我们都知道有26个设计模式,但是大多停留在概念层面,真实开发中很少遇到,Mybatis源码中使用了大量的设计模式,阅读源码并观察设计模式在其中的应用,能够更深入的理解设计模式. Mybatis至少 ...
随机推荐
- SpringMVC基础学习(一)—初识SpringMVC
一.HelloWorld 1.导入SpringMVC所需的jar包 2.配置web.xml 配置DispatcherServlet.DispatcherServlet默认加载/ ...
- 数字图像处理(MATLAB版)学习笔记(1)——第1章 绪言
0.下定决心 当当入手数字图像处理一本,从此开此正式跨入数字图像处理大门.以前虽然多多少少接触过这些东西,也做过一些相关的事情,但感觉都不够系统,也不够专业,从今天开始,一步一步地学习下去,相信会有成 ...
- Xcode8.3 添加iOS10.3以下旧版本模拟器
问题起源 由于手边项目需要适配到iOS7, 但是手边的测试机都被更新到最新版本,所以有些潜在的bug,更不发现不了.最近就是有个用户提出一个bug,而且是致命的,app直接闪退.app闪退,最常见的无 ...
- kafka各个版本特性预览介绍
kafka-0.8.2 新特性 producer不再区分同步(sync)和异步方式(async),所有的请求以异步方式发送,这样提升了客户端效率.producer请求会返回一个应答对象,包括偏移量或者 ...
- MySQL关于Duplicate entry '1' for key 'PRIMARY'错误
今天复习MySQL遇到Duplicate entry '1' for key 'PRIMARY'错误. 原因是主键值为'1'的数据已经存在,主键是唯一的,不可重复.
- org.gradle.api.internal.tasks.DefaultTaskInputs$TaskInputUnionFileCollection cannot be cast to org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection
转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6709758.html Android Studio导入项目报错: org.gradle.api.inter ...
- 图文详解如何快捷搭建LNMP服务环境
上一篇与大家一起学习了下如何搭建LAMP环境的知识,今天小编再和大家分享下如何快捷地搭建LNMP环境,并搭建起一个网站.Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/S ...
- Liunx的DHCP配置
1.DHCP简介 (1)DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个简化主机IP地址分配管理的TCP/IP标准协议,用户可以利用DHCP服 ...
- Python 操作 MySQL 的正确姿势
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:邵建永 使用Python进行MySQL的库主要有三个,Python-MySQL(更熟悉的名字可能是MyS ...
- jQuery选择器与CSS选择器
1. 通过位置选择的几个操作: :first:默认情况下是相对整个页面来说的第一个,如:li:first表示整个页面的第一个li元素,而ul li:first表示整个页面的第一个li元素,并且是在ul ...