mybatis $和#源代码分析

JDBC中,主要使用两种语句,一种是支持参数化和预编译的PreparedStatement,支持原生sql,支持设置占位符,参数化输入的参数,防止sql注入攻击,在mybatis的mapper配置文件中,我们通过使用#和$告诉mybatis我们需要对参数进行怎样的设置。sql注入指的是利用现有应用程序将恶意的sql命令提交存在安全漏洞。例如在提交表单时加入or拼接语句使其永远成立。对比JDBC执行流程connection->statement->result,在mybatis中由SqlSession提供给用户操作的API,Excutor具体执行数据库的操作。

SqlSession接口主要的实现类有:

先看DefaultSqlSession:

selectOne的方法最终转化成了selectList方法

最终执行的是箭头指的selectList方法,该方法中含有变量:MappedSatement和Excutor,MappedSatement是加载mapper.xml时匹配的namespace+id

StrictMap是Configuration类中的一个静态内部类,继承了HashMap,看一下selectList方法调用的wrapCollection方法

这就是为什么我们在mapper.xml中foreach用list或array遍历,再看Excutor执行的query方法,Excutor接口实现类

BaseExcutor的query方法:

由上可知若设置清除缓存,首先会清除缓存,首先会根据CacheKey查找缓存,查找结果为空,则从数据库查

 

doQuery:

可见Executor委托给StatementHandler执行查询,在此之前有一个预编译的过程(prepareStatement方法),StatementHandler接口的实现类:CallableStatementHandler,PreparedStatementHandler,SimpleStatementHandler对应JDBC中的CallableStatement,PreparedStatement和Statement,分别的执行方法:

看之前提到的prepareStatement方法

handler会对statement参数化设置,PreparedStatementHandler中:

由parameterhandler执行参数设置,上面是简单分析的查询流程,回头说$和#,我们知道,使用$时statement执行的是拼接操作,#的时候statement用的是占位符 ?,这是mybatis解析的时候造成的,根据测试例子:

SqlSessionFactoryBuilder的build方法:

XMLConfigBuilder负责解析总配置文件,其中方法有:

返回值都为XNode节点类型,看mapperElement:

package扫描包,resource和class扫描指定类和mapper.xml,XMLMapperBuilder:

主要看这两个方法,根据节点建立statement:

由上可以看出XMLStatementBuilder解析statement,也就是mapper.xml中的一个个statement,快成功了。。。

parseStatementNode解析一条记录中的各个属性,例如resultType,parameterType,useCache等等。。。该方法代码过长,其主要在:

两处,一个是SqlSource,一个是addMappedStatement,解析的属性值都对应到MappedStatement对象中

在MappedStatement对象中除这些外,还有个属性SqlSource,可见该对象决定sql语句的解析

只有一个抽象方法getBoundSql,SqlSource是如何获取的呢,这就用到了上面prepareStatement方法中的LanguageDriver的createSqlSource方法,继续跟进:

解析sql语句之前会先解析selectKey和include节点,LanguageDriver的实现类有XMLLanguageDriver和RawLanguageDriver

可以知道createSqlSource方法只在XMLLanguageDriver实现

委托给了XMLScriptBuilder的parseScriptNode方法:

 1 public SqlSource parseScriptNode() {
 2     List<SqlNode> contents = parseDynamicTags(context);
 3     MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
 4     SqlSource sqlSource = null;
 5     if (isDynamic) {
 6       sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
 7     } else {
 8       sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
 9     }
10     return sqlSource;
11   }

根据isDynamic标志确定sqlSource类型,parseDynamicTags方法:

 1  private List<SqlNode> parseDynamicTags(XNode node) {
 2     List<SqlNode> contents = new ArrayList<SqlNode>();
 3     NodeList children = node.getNode().getChildNodes();
 4     for (int i = 0; i < children.getLength(); i++) {
 5       XNode child = node.newXNode(children.item(i));
 6       if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
 7         String data = child.getStringBody("");
 8         TextSqlNode textSqlNode = new TextSqlNode(data);
 9         if (textSqlNode.isDynamic()) {
10           contents.add(textSqlNode);
11           isDynamic = true;
12         } else {
13           contents.add(new StaticTextSqlNode(data));
14         }
15       } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
16         String nodeName = child.getNode().getNodeName();
17         NodeHandler handler = nodeHandlers.get(nodeName);
18         if (handler == null) {
19           throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
20         }
21         handler.handleNode(child, contents);
22         isDynamic = true;
23       }
24     }
25     return contents;
26   }

返回值是一个list,过程:

 遍历各个子节点

(1) 如果节点类型是文本或者CDATA,构造一个TextSqlNode或StaticTextSqlNode

(2) 如果节点类型是元素,说明该节点是个动态sql,然后会使用NodeHandler处理各个类型的子节点。这里的NodeHandler是XMLScriptBuilder的一个内部接口,其实现类包括TrimHandler、WhereHandler、SetHandler、IfHandler、ChooseHandler等。看类名也就明白了这个Handler的作用,比如我们分析的trim节点,对应的是TrimHandler;if节点,对应的是IfHandler...,TextSqlNode的isDynamic方法:

1   public boolean isDynamic() {
2     DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
3     GenericTokenParser parser = createParser(checker);//建立GenericTokenParser
4     parser.parse(text);//GenericTokenParser解析text
5     return checker.isDynamic();
6   }

createParser方法:

 1 private GenericTokenParser createParser(TokenHandler handler) { 2 return new GenericTokenParser("${", "}", handler); 3 } 

构造方法:

1   public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
2     this.openToken = openToken;
3     this.closeToken = closeToken;
4     this.handler = handler;
5   }

根据是否Dynamic,TokenHandler的主要实现类有:DynamicCheckerTokenParser和ParameterMappingTokenHandler,VariableTokenHandler

 1 private GenericTokenParser createParser(TokenHandler handler) { 2 return new GenericTokenParser("${", "}", handler); 3 } //$的处理方式

DynamicCheckerTokenParser实现的handleToken方法

1  public String handleToken(String content) {
2       this.isDynamic = true;
3       return null;
4     }

ParameterMappingTokenHandler实现的handleToken方法:

1  public String handleToken(String content) {//#的处理方式,返回占位符?
2       parameterMappings.add(buildParameterMapping(content));
3       return "?";
4     }

#的方式大概就是这样,再看$,$使用的是DynamicCheckerTokenParser,这时候再看返回的DynamicSqlSource,其实现SqlSource接口的getBoundSql方法:

 1 public BoundSql getBoundSql(Object parameterObject) {
 2     DynamicContext context = new DynamicContext(configuration, parameterObject);
 3     rootSqlNode.apply(context);//apply方法实际调用的是TextSqlNode的applay方法
 4     SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
 5     Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
 6     SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());//SqlSourceBuilder的parse方法解析
 7     BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
 8     for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
 9       boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
10     }
11     return boundSql;
12   }
TextSqlNode的apply方法:
1 public boolean apply(DynamicContext context) {
2     GenericTokenParser parser = createParser(new BindingTokenParser(context));
3     context.appendSql(parser.parse(text));
4     return true;
5   }

用的是BindingTokenParser的parse方法:

 1  public String parse(String text) {
 2     StringBuilder builder = new StringBuilder();
 3     if (text != null && text.length() > 0) {
 4       char[] src = text.toCharArray();
 5       int offset = 0;
 6       int start = text.indexOf(openToken, offset);
 7       while (start > -1) {
 8         if (start > 0 && src[start - 1] == '\\') {
 9           // the variable is escaped. remove the backslash.
10           builder.append(src, offset, start - 1).append(openToken);
11           offset = start + openToken.length();
12         } else {
13           int end = text.indexOf(closeToken, start);
14           if (end == -1) {
15             builder.append(src, offset, src.length - offset);
16             offset = src.length;
17           } else {
18             builder.append(src, offset, start - offset);
19             offset = start + openToken.length();
20             String content = new String(src, offset, end - offset);
21             builder.append(handler.handleToken(content));//又回到了handleToken方法,此时的handler为BindingTokenParser
22             offset = end + closeToken.length();
23           }
24         }
25         start = text.indexOf(openToken, offset);
26       }
27       if (offset < src.length) {
28         builder.append(src, offset, src.length - offset);
29       }
30     }
31     return builder.toString();
32   }

BindingTokenParser的handleToken:

 1 public String handleToken(String content) {
 2       Object parameter = context.getBindings().get("_parameter");
 3       if (parameter == null) {
 4         context.getBindings().put("value", null);
 5       } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
 6         context.getBindings().put("value", parameter);//从此处可以看出mapper.xml中$或#中可以用value
 7       }
 8       Object value = OgnlCache.getValue(content, context.getBindings());//此处用了ognl处理
 9       return (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"
10     }

ognl不太清除,先分析到这



转载于:https://www.cnblogs.com/miserable-faith/p/7658550.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/454375.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git 命令详解和常见问题解决

功能一 提交&#xff1a;1:git init # 初始化&#xff0c;表示即将对当前文件夹进行版本控制2:git status # 查看Git当前状态&#xff0c;如&#xff1a;那些文件被修改过、那些文件还未提交到版本库等。3:git add . # 添加当前目录下所有文件到版本…

辞职日记----记录31岁的程序员跳槽心态

vcleaner http://topic.csdn.net/u/20080626/23/8f6a8ecc-c072-43ee-bf2d-7ac2286b6805.html http://topic.csdn.net/u/20080704/23/858fc00d-ec14-4db7-93be-34903b7f157a.html 转载他的离职日记&#xff0c;有许多东西值得我们认真思考&#xff0c;人活着到底为了什么&a…

从Android源码的角度分析Binder机制

IPC 为了弄懂IPC的来龙去脉&#xff0c;我将从以下三个方面为大家来讲解&#xff0c;希望对大家理解IPC会有帮助 什么是IPC IPC是Inter Process Communication的缩写&#xff0c;其意思就是进程间的通信&#xff0c;也就是两个进程之间的通信过程。我们都知道在Android系统中&a…

excel vba 调用webbrowser_VBA 公式与函数

一, 在单元格中输入公式的3种方法:1) 用VBA在单元格中输入普通公式Sub formula_1() Range("d2") ("B2 * C2") End Sub运行程序后,在D2的单元格内显示的是公式 B2 * C2 ,并非程序返回值.下文(二)中会介绍另外一种直接返回值的方式想要通过程序一…

内部类可以引用它的包含类的成员吗?有没有什么限制?

最近看到一道面试题&#xff1a;内部类可以引用它的包含类的成员吗&#xff1f;有没有什么限制&#xff1f; 答案大部分都是这样子的&#xff1a; 完全可以。如果不是静态内部类&#xff0c;那没有什么限制&#xff01; 一个内部类对象可以访问创建它的外部类对象的成员包括私有…

松下NPM服务器怎么备份系统,松下(Panasonic)-NPM校正amp;CPK完整版教程,一步步带你成为SMT设备大神!...

马上注册&#xff0c;结交更多技术专家&#xff0c;享用更多功能&#xff0c;让你轻松解决各种三星贴片机问题您需要 登录 才可以下载或查看&#xff0c;没有帐号&#xff1f;立即注册 xa8f80375060fa05b8aebe69ffa21080c.gif (5.26 KB, 下载次数: 3)2019-8-12 00:02 上传f5aae…

Python 模块之科学计算 Pandas

目录 一、Pandas简介 数据结构 二、Series series 的创建 Series值的获取 Series的运算 Series缺失值检测 Series自动对齐 Series及其索引的name属性 三、DataFrame 创建 Index对象 通过索引值或索引标签获取数据 自动化对齐 四、文件操作 文件读取 数据库数据…

根据 设备名(br0/eth0/em0)称获取 当前机器的IP地址与子网掩码信息

#!/usr/bin/env python 根据 设备名(br0/eth0/em0)称获取 当前机器的IP地址与子网掩码信息import socket, struct, fcntldef get_ipaddress(ifname eth0):s socket.socket(socket.AF_INET, socket.SOCK_DGRAM)return socket.inet_ntoa(fcntl.ioctl(s.fileno(),0x8915, # SI…

我的程序生涯

本文仅为爱好程序及向往真正之程序员者所作&#xff0c;其余人等可忽略下文。 如今&#xff0c;接触CS几近八年&#xff0c;不学无术&#xff0c;所精之物鲜也&#xff0c;以至一事无成。 现回忆吾程序之生涯&#xff0c;以整理繁杂之心绪。 1. 接触计算机和编程语言 02年始大…

机器学习中qa测试_如何对机器学习做单元测试

作者&#xff1a;Chase Roberts编译&#xff1a;ronghuaiyang导读养成良好的单元测试的习惯&#xff0c;真的是受益终身的&#xff0c;特别是机器学习代码&#xff0c;有些bug真不是看看就能看出来的。在过去的一年里&#xff0c;我把大部分的工作时间都花在了深度学习研究和实…

项目宝提供的服务器,开源WebSocket服务器项目宝贝鱼CshBBrain V4.0.1 和 V2.0.2发布

开源WebSocket服务器项目宝贝鱼CshBBrain V4.0.1 和 V2.0.2发布更新的功能列表如下&#xff1a;1.解决开启广播消息开关时&#xff0c;不能同时接入2个客户端的重大缺陷。2.对广播消息做了重大优化&#xff0c;从以前一个线程发送广播消息进化到使用工作线程池中的线程并行的发…

c# 无损高质量压缩图片代码

/// 无损压缩图片 /// <param name"sFile">原图片</param> /// <param name"dFile">压缩后保存位置</param> /// <param name"dHeight">高度</param> /// <param name"dWidth"…

一个从文本文件里“查找并替换”的功能

12345678910111213141516171819202122232425# -*- coding: UTF-8 -*-file input("请输入文件路径:") word1 input("请输入要替换的词:") word2 input("请输入新的词&#xff1a;") fopen(file,"r") AAAf.read() count 0 def BBB()…

机器学习算法之 KNN

K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了&#xff0c;在我们平常的生活中也会不自主的应用。比如&#xff0c;我们判断一个人的人品&#xff0c;只需要观察他来往最密切的几个人的人品好坏就可以得出了。这里就运用了KNN的思想。KNN方法既可以做分类&…

安装云端服务器操作系统,安装云端服务器操作系统

安装云端服务器操作系统 内容精选换一换SAP云服务器规格在申请SAP ECS之前&#xff0c;请参考SAP标准Sizing方法进行SAPS值评估&#xff0c;并根据Sizing结果申请云端ECS服务器资源&#xff0c;详细信息请参考SAP Quick Sizer。SAP 各组件最低硬盘空间、RAM&#xff0c;以及软件…

python 进度条_六种酷炫Python运行进度条

转自&#xff1a;一行数据阅读文本大概需要 3 分钟你的代码进度还剩多少&#xff1f;今天给大家介绍下目前6种比较常用的进度条&#xff0c;让大家都能直观地看到脚本运行最新的进展情况。1.普通进度条2.带时间进度条3.tpdm进度条4.progress进度条5.alive_progress进度条6.可视…

js 获取多少天前

getBeforeDate: function(day, str) { var now new Date().getTime(); //获取毫秒数 var before new Date(now - ((day > 0 && day ? day : 0) * 86400 * 1000)); var year before.getFullYear(); var month before.getMonth()1; var date before.getDate(); …

程序员的基本素质

给所有立志成为程序员的朋友 以及 自勉之&#xff01; 程序员基本素质&#xff1a; 作一个真正合格的程序员&#xff0c;或者说就是可以真正合格完成一些代码工作的程序员&#xff0c;应该具有的素质。 1&#xff1a;团队精神和协作能力 把它作为基本素质&#xff0c;并…

权限之浅理解

白马过隙&#xff0c;在感叹时光流逝的同时不得不承认在学习中随着知识面的不断扩展所接受的东西也越来越多&#xff0c;尤其是那些外形比较容易混淆的命令&#xff0c;着实让作为新手的吃了很多苦头&#xff0c;趁着学习紧张之时偷个懒整理这周易混淆的命令&#xff1a; chgrp…

机器学习算法之生成树

一、什么是决策树&#xff1f; 决策树&#xff08;Decision Tree&#xff09;是一种基本的分类和回归的方法。 分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点&#xff08;node&#xff09;和有向边&#xff08;directed edge&#xff09;组成。结点有两种…