当前位置: 首页 > news >正文

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方法。

 1 //org.apache.ibatis.session.defaults.DefaultSqlSession
 2 ……
 3   @Override
 4   public <E> List<E> selectList(String statement) {
 5     return this.selectList(statement, null);
 6   }
 7 
 8   @Override
 9   public <E> List<E> selectList(String statement, Object parameter) {
10     return this.selectList(statement, parameter, RowBounds.DEFAULT);
11   }
12 
13   @Override
14   public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
15     try {
16       MappedStatement ms = configuration.getMappedStatement(statement);
17       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
18     } catch (Exception e) {
19       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
20     } finally {
21       ErrorContext.instance().reset();
22     }
23   }
24 ……

我们看到关于selectList有三个重载方法,最后调用的都是public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)。在此方法中第一个参数为String类型且命名为statement,第二个参数为Object命名为parameter,回到MapperMethod。

 1 //org.apache.ibatis.binding.MapperMethod
 2  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
 3     List<E> result;
 4     Object param = method.convertArgsToSqlCommandParam(args);
 5     if (method.hasRowBounds()) {
 6       RowBounds rowBounds = method.extractRowBounds(args);
 7       result = sqlSession.<E>selectList(command.getName(), param, rowBounds);  //注意传递了什么参数进来
 8     } else {
 9       result = sqlSession.<E>selectList(command.getName(), param);
10     }
11     // issue #510 Collections & arrays support
12     if (!method.getReturnType().isAssignableFrom(result.getClass())) {
13       if (method.getReturnType().isArray()) {
14         return convertToArray(result);
15       } else {
16         return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
17       }
18     }
19     return result;
20   }

  第7行代码可以看到传递了什么参数进来,跟踪command.getName()是怎么来的。

private final SqlCommand command;

  看来是一个叫做SqlCommand的变量,进而我们可以发现这个SqlCommand是MapperMethod的静态内部类。

 1 //org.apache.ibatis.binding.MapperMethod
 2   public static class SqlCommand {
 3 
 4     private final String name;
 5     private final SqlCommandType type;
 6 
 7     public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
 8       String statementName = mapperInterface.getName() + "." + method.getName();  //获取接口+方法名
 9       MappedStatement ms = null;  //定义一个MappedStatement,这个MapperedStatement稍后介绍
10       if (configuration.hasStatement(statementName)) {  //从Configuration对象查找是否有这个方法的全限定名称
11         ms = configuration.getMappedStatement(statementName);  //有,则根据方法的全限定名称获取MappedStatement实例
12       } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35//如果没有在Configuration对象中找到这个方法,则向上父类中获取全限定方法名
13         String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
14         if (configuration.hasStatement(parentStatementName)) {  //在向上的父类查找是否有这个全限定方法名
15           ms = configuration.getMappedStatement(parentStatementName);   //有,则根据方法的全限定名称获取MappedStatement实例
16         }
17       }
18       if (ms == null) {
19         if(method.getAnnotation(Flush.class) != null){
20           name = null;
21           type = SqlCommandType.FLUSH;
22         } else {
23           throw new BindingException("Invalid bound statement (not found): " + statementName);
24         }
25       } else {
26         name = ms.getId();  //这个ms.getId,其实就是我们在mapper.xml配置文件中配置一条sql语句时开头的<select id="……"……,即是接口的该方法名全限定名称
27         type = ms.getSqlCommandType();  //显然这是将sql是何种类型(insert、update、delete、select)赋给type
28         if (type == SqlCommandType.UNKNOWN) {
29           throw new BindingException("Unknown execution method for: " + name);
30         }
31       }
32     }
33 
34     public String getName() {
35       return name;
36     }
37 
38     public SqlCommandType getType() {
39       return type;
40     }
41   }

  大致对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的公众号

相关文章:

  • 对最近一段时间的总结
  • [工具]ps
  • 快速查询数据库中有数据的表
  • 一段来自地震前方记者的QQ聊天记录
  • 汶川地震牵动我全家人的心
  • Flask 模板语言
  • c#.winform,datagridview,数组,绑定,字符串,字符串数组绑定datagridview显示,长度,显示数组内容...
  • 谷歌livereload插件使用
  • 如何划超网
  • linux mono环境
  • GroupBy的用法
  • 直接用postman测试api ,服务器端没提供跨域也可以访问。
  • MySQL中实现分割字符串的方法
  • Tomcat日志问题
  • 日常生活中用的六个加快XP运行方法
  • 30天自制操作系统-2
  • Akka系列(七):Actor持久化之Akka persistence
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • JAVA_NIO系列——Channel和Buffer详解
  • Java多线程(4):使用线程池执行定时任务
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • rabbitmq延迟消息示例
  • React-生命周期杂记
  • Redux 中间件分析
  • vue脚手架vue-cli
  • 前端性能优化——回流与重绘
  • 少走弯路,给Java 1~5 年程序员的建议
  • 实现菜单下拉伸展折叠效果demo
  • 手机端车牌号码键盘的vue组件
  • 微信小程序:实现悬浮返回和分享按钮
  • 消息队列系列二(IOT中消息队列的应用)
  • 用简单代码看卷积组块发展
  • 06-01 点餐小程序前台界面搭建
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • #NOIP 2014# day.1 T2 联合权值
  • $(selector).each()和$.each()的区别
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (C语言)共用体union的用法举例
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (二)构建dubbo分布式平台-平台功能导图
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (分享)自己整理的一些简单awk实用语句
  • (九)One-Wire总线-DS18B20
  • (三)docker:Dockerfile构建容器运行jar包
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (五)MySQL的备份及恢复
  • (一) springboot详细介绍
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • ***检测工具之RKHunter AIDE
  • .net core控制台应用程序初识
  • .net Signalr 使用笔记