Mybatis3源码分析(05)-加载Configuration-加载MappedStatement
2019独角兽企业重金招聘Python工程师标准>>>
Mybatis3源码分析(05)-加载Configuration-加载MappedStatement 博客分类: java mybatis
MappedStatement说明
- //节点中的id属性加要命名空间
- private String id;
- //直接从节点属性中取
- private Integer fetchSize;
- //直接从节点属性中取
- private Integer timeout;
- private StatementType statementType;
- private ResultSetType resultSetType;
- //对应一条SQL语句
- private SqlSource sqlSource;
- //每条语句都对就一个缓存,如果有的话。
- private Cache cache;
- //这个已经过时了
- private ParameterMap parameterMap;
- private List<ResultMap> resultMaps;
- private boolean flushCacheRequired;
- private boolean useCache;
- private boolean resultOrdered;
- //SQL的类型,select/update/insert/detete
- private SqlCommandType sqlCommandType;
- private KeyGenerator keyGenerator;
- private String[] keyProperties;
- private String[] keyColumns;
- //是否有内映射
- private boolean hasNestedResultMaps;
- private String databaseId;
- private Log statementLog;
- private LanguageDriver lang;
- private String[] resultSets;
上面属性都比较简单,复杂的是SqlSource,下面有详细的描述!
XMLStatementBuilder.parseStatementNode()方法
- private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
- for (XNode context : list) {
- //一个select/update/insert/delete元素创建一个XMLStatementBuilder对象
- final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
- try {
- //将元素解析成MappedStatemenet对象,并加入到Configuration中去
- statementParser.parseStatementNode();
- } catch (IncompleteElementException e) {
- configuration.addIncompleteStatement(statementParser);
- }
- }
如下是parseStatementNode()方法的代码
- public void parseStatementNode() {
- String id = context.getStringAttribute("id");
- String databaseId = context.getStringAttribute("databaseId");
- if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
- Integer fetchSize = context.getIntAttribute("fetchSize");
- Integer timeout = context.getIntAttribute("timeout");
- String parameterMap = context.getStringAttribute("parameterMap");
- String parameterType = context.getStringAttribute("parameterType");
- Class<?> parameterTypeClass = resolveClass(parameterType);
- String resultMap = context.getStringAttribute("resultMap");
- String resultType = context.getStringAttribute("resultType");
- String lang = context.getStringAttribute("lang");
- LanguageDriver langDriver = getLanguageDriver(lang);
- Class<?> resultTypeClass = resolveClass(resultType);
- String resultSetType = context.getStringAttribute("resultSetType");
- //Statement的类型,对应jdbc里的三个类型:Statement、PreparedStatement、CallableStatement,默认使用PreparedStatement
- StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
- //这个也是跟jdbc里相对应的,一般采用默认即可
- ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
- //Sql的类型,select/update/insert/delete
- String nodeName = context.getNode().getNodeName();
- SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
- boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
- //是否刷新缓存
- boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
- //是否使用缓存
- boolean useCache = context.getBooleanAttribute("useCache", isSelect);
- boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
- //不做分析
- // Include Fragments before parsing
- XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
- includeParser.applyIncludes(context.getNode());
- //不做分析
- // Parse selectKey after includes and remove them.
- processSelectKeyNodes(id, parameterTypeClass, langDriver);
- //生成SqlSource对象,这个对象非常重要,接下来详细分析
- // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
- SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
- String resultSets = context.getStringAttribute("resultSets");
- String keyProperty = context.getStringAttribute("keyProperty");
- String keyColumn = context.getStringAttribute("keyColumn");
- //自动生成key,这里也不做讨论
- KeyGenerator keyGenerator;
- String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
- keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
- if (configuration.hasKeyGenerator(keyStatementId)) {
- keyGenerator = configuration.getKeyGenerator(keyStatementId);
- } else {
- keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
- configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
- ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
- }
- //生成MappedStatement对象,并加到Configuration中
- builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
- fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
- resultSetTypeEnum, flushCache, useCache, resultOrdered,
- keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
- }
上在方法里附件解析一些基本的属性外还有两个主要的部分
- SqlSource的构建过程
[java] view plain copy
- SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
- MappedStatement的构建过程
[java] view plain copy
- builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
- fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
- resultSetTypeEnum, flushCache, useCache, resultOrdered,
- keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
SqlSource构建过程
SqlSource接口
- /**
- * Represents the content of a mapped statement read from an XML file or an annotation.
- * It creates the SQL that will be passed to the database out of the input parameter received from the user.
- *
- * @author Clinton Begin
- */
- public interface SqlSource {
- BoundSql getBoundSql(Object parameterObject);
- }
SqlSource表示从mapper.xml或注解中读取的sql内容,该sql一般还不能都被直接执行,例如
- <select id="selectUserDetail" resultMap="detailUserResultMap">
- <!--CDATA里内容会都解析成一个SqlSource对象-->
- <![CDATA[
- select user_id,user_name,user_type,cust_id from tf_f_user a where a.user_id=#${userId}
- ]]>/select>
SqlSource只有一个方法:getBoundSql(paramenterObject),其中paramenterObject为运行sql里的实际参数
BoundSql
- /**
- * An actual SQL String got form an {@link SqlSource} after having processed any dynamic content.
- * The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings
- * with the additional information for each parameter (at least the property name of the input object to read
- * the value from).
- * </br>
- * Can also have additional parameters that are created by the dynamic language (for loops, bind...).
- */
- /**
- * @author Clinton Begin
- */
- public class BoundSql {
- private String sql;
- private List<ParameterMapping> parameterMappings;
- private Object parameterObject;
- private Map<String, Object> additionalParameters;
- private MetaObject metaParameters;
- public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
- this.sql = sql;
- this.parameterMappings = parameterMappings;
- this.parameterObject = parameterObject;
- this.additionalParameters = new HashMap<String, Object>();
- this.metaParameters = configuration.newMetaObject(additionalParameters);
- }
- public String getSql() {
- return sql;
- }
- public List<ParameterMapping> getParameterMappings() {
- return parameterMappings;
- }
- public Object getParameterObject() {
- return parameterObject;
- }
- public boolean hasAdditionalParameter(String name) {
- return metaParameters.hasGetter(name);
- }
- public void setAdditionalParameter(String name, Object value) {
- metaParameters.setValue(name, value);
- }
- public Object getAdditionalParameter(String name) {
- return metaParameters.getValue(name);
- }
- }
SqlBound代码并不多,就是一个普通的java对象,有两个属性非常重要
- sql:看代码里的注解,这个sql已经是经过了一些处理,可以被jdbc执行的了。xml里配置的sql可能有占位符#{username},这里的sql占位符已经被替换成"?"号了。
- parameterMappings:执行sql对象的实际的参数。由此可以判断,每执行一条sql都会创建一个BoundSql对象。
LanguageDriver
- public Configuration() {
- ...
- languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
- languageRegistry.register(RawLanguageDriver.class);
- }
- public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
- XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
- return builder.parseScriptNode();
- }
XMLScriptBuilder
XMLScriptBuilder.parseScriptNode()方法
- public SqlSource parseScriptNode() {
- //将一个sql内容解析成多个SqlNode
- List<SqlNode> contents = parseDynamicTags(context);
- //将多个SqlNode组合一个SqlNode
- MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
- SqlSource sqlSource = null;
- //判断sql是否是动态的
- if (isDynamic) {
- //生成动态的SqlSource
- sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
- } else {
- //生成静态的SqlSource
- sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
- }
- return sqlSource;
- }
再看parseDynamicTagS(context)方法
- private List<SqlNode> parseDynamicTags(XNode node) {
- //一个sql会被解析成多个SqlNode,稍后会有示例详细说明
- List<SqlNode> contents = new ArrayList<SqlNode>();
- NodeList children = node.getNode().getChildNodes();
- for (int i = 0; i < children.getLength(); i++) {
- XNode child = node.newXNode(children.item(i));
- if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
- //如果这个Node只包含文本
- String data = child.getStringBody("");
- //生成一个TextSqlNode
- TextSqlNode textSqlNode = new TextSqlNode(data);
- //判断是否是动态的,如果文本里包含占位符,如#{username}或{table_name},isDynamic()方法就会返回true
- if (textSqlNode.isDynamic()) {
- contents.add(textSqlNode);
- isDynamic = true;
- } else {
- contents.add(new StaticTextSqlNode(data));
- }
- } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
- //如果是有xml标签的Node,交由Handler处理,同时被认为是动态的
- String nodeName = child.getNode().getNodeName();
- NodeHandler handler = nodeHandlers.get(nodeName);
- if (handler == null) {
- throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
- }
- handler.handleNode(child, contents);
- isDynamic = true;
- }
- }
- return contents;
- }
再看看nodeHandlers都有那些
- private Map<String, NodeHandler> nodeHandlers = new HashMap<String, NodeHandler>() {
- private static final long serialVersionUID = 7123056019193266281L;
- {
- //Mybatis3动态sql都支持那些配置,这里就很清楚啦
- put("trim", new TrimHandler());
- put("where", new WhereHandler());
- put("set", new SetHandler());
- put("foreach", new ForEachHandler());
- put("if", new IfHandler());
- put("choose", new ChooseHandler());
- put("when", new IfHandler());
- put("otherwise", new OtherwiseHandler());
- put("bind", new BindHandler());
- }
- };
看到这里基本上能了解sql是怎么被解析的啦!举例说明:
- <select id="selectUserDetail" resultMap="detailUserResultMap">
- <![CDATA[
- select user_id,user_name,user_type,cust_id --这里一行会解析成一个StaticTextSqlNode
- from tf_f_user a --这里一行也会解析成一个StaticTextSqlNode
- where a.user_id=#{userId} --这行会被解析成TextSqlNode,并且isDynamic被设置成true,因为有占位符
- --这个空行也解析成一个StaticTextSqlNode
- ]]><!-- 这四个SqlNode会被组合成一个MixedSqlNode -->
- </select>
再来个动态sql的:
- <select id="selectUserDetail" resultMap="detailUserResultMap">
- <![CDATA[
- select user_id,user_name,user_type,cust_id --这里一行会解析成一个StaticTextSqlNode
- from tf_f_user a --这里一行也会解析成一个StaticTextSqlNode
- where a.user_id=#{userId} --这行会被解析成TextSqlNode,并且isDynamic被设置成true,因为有占位符
- --这个空行也解析成一个StaticTextSqlNode
- ]]>
- <if test="user_name!=null"> <!-- 这个标签里的内容会交给IfHandler处理 -->
- and --这里的解析与上行的一样,解析成一个StaticTextSqlNode
- user_name=#{userName} --这里的解析与上行的一样,也会被解析成一个TextSqlNode,并且isDynamic被设置成true,因为有占位符
- </if><!-- IfHandler会将这里面的内个SqlNode组成MixedSqlNode再组成一个IfSqlNode -->
- </select><!-- 这五个SqlNode会被组合成一个MixedSqlNode -->
附上IfHandler的代码
- private class IfHandler implements NodeHandler {
- public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
- //解析子节点
- List<SqlNode> contents = parseDynamicTags(nodeToHandle);
- //组合
- MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
- String test = nodeToHandle.getStringAttribute("test");
- //生成IfSqlNode
- IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
- targetContents.add(ifSqlNode);
- }
- }
其他的nodeHandler在这里就不讨论了,实现方式与IfHandler差不多。如下两个方法也不在这里做讨论
- SqlSource.getBoundSql()方法
- SqlNode.apply(DynamicContextcontext)方法
http://blog.csdn.net/ashan_li/article/details/50351080
转载于:https://my.oschina.net/xiaominmin/blog/1599100
Mybatis3源码分析(05)-加载Configuration-加载MappedStatement相关推荐
- Mybatis 源码分析(一)配置文件加载流程
Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...
- 【SA8295P 源码分析】08 - XBL Loader 加载 SMSS、XBL Config、SHRM、CDT 、DDR、APDP、RamDump、OEM_MISC、AOP、QSEE过程分析
[SA8295P 源码分析]08 - XBL Loader 加载 SMSS.XBL Config.SHRM.CDT .DDR.APDP.RamDump.OEM_MISC.AOP.QSEE Dev Co ...
- Tomcat源码分析——server.xml文件的加载
前言 作为Java程序员,对于tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载的进行分析. 源码分析 Bootstrap的 ...
- thinkphp源码分析(三)—自动加载篇(Loader的分析)
源码分析 自动加载 系统会调用 Loader::register()方法注册自动加载,在这一步完成后,所有符合规范的类库(包括Composer依赖加载的第三方类库)都将自动加载. 系统的自动加载由下面 ...
- Tomcat7.0源码分析——server.xml文件的加载与解析
前言 作为Java程序员,对于Tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载和解析进行分析. 加载过程分析 Bootst ...
- SpringMVC源码分析_1 SpringMVC容器启动和加载原理
SpringMVC源码分析_1 SpringMVC启动和加载原理 ...
- 云客Drupal源码分析之配置系统Configuration(一)
各位<云客drupal源码分析>系列的读者: 本系列一直以每周一篇的速度进行博客原创更新,希望帮助大家理解drupal8底层原理,并缩短学习时间,但自<插件系统(上)>主题开始 ...
- flume源码分析2--配置文件的加载
上面提到Application启动的时候,PollingPropertiesFileConfigurationProvider作为唯一的LifecycleAware类型的组件被交给监护者Lifecyc ...
- llvm libLLVMCore源码分析 05 - Instruction Class
源码路径 llvm\include\llvm\IR\Instruction.h llvm\include\llvm\IR\Instruction.def llvm\include\llvm\IR\In ...
最新文章
- python可以做什么工作好-学Python能找到什么工作?这4种工作最热门!
- vmware上给根分区增加空间以及创建逻辑卷
- 以太坊知识教程------交易
- docker18.09.3修改守护进程配置文件不生效问题
- Spark _29_SparkStreaming初始
- python爬虫网络请求超时是什么意思_python爬虫怎么处理异常和超时?
- Spring Security登录
- IMPDP导入实例(oracle)
- java treeset比较,java中TreeSet的两种排序比较的方式
- Hadoop平台搭建
- python 邻接矩阵_阿里巴巴举荐,Python视频,免费分享,用python求解特征向量和拉普拉斯矩阵...
- js做四则运算时,精度丢失问题及解决方法
- tcp长连接java_聊聊 TCP 长连接和心跳那些事
- mysql linux查看配置文件my.cnf位置
- 【三维路径规划】基于matlab A_star算法无人机三维路径规划【含Matlab源码 1387期】
- js html 导出excel文件,js 导出excel ,elsx格式
- 基于Springboot的Java邮件系统的设计与实现(附论文和源码)
- win10激活方法(无需下载)
- 蓝牙核心规范(V5.2)3.2-深入详解之数据传输架构
- 联合国首席AI顾问专访:我们期望AI应该是完美的,但这永远不会
热门文章
- 安装Adventure Works 2008 R2演示数据库
- 在织梦模板中不适用ajax,直接用标签也能调用当前会员的信息
- 华人“军火专家”——黄仁勋 1
- 微信小程序教程 [第五篇]脚本语言(WXS)
- 戴尔:发力互联互通 构建世界基础设施中心
- WEB中会话跟踪[转]
- BZOJ 3224: Tyvj 1728 普通平衡树 treap
- MS CRM 2011 RC中的新特性(2)——销售自动化方面
- VLAN系列二: 实现VLAN的机制
- asp.net(c#) linkbutton CommandArgument