2019独角兽企业重金招聘Python工程师标准>>>

Mybatis3源码分析(05)-加载Configuration-加载MappedStatement 博客分类: java mybatis

MappedStatement说明

一个MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条SQL语句。其属性有
[java] view plain copy
  1. //节点中的id属性加要命名空间
  2. private String id;
  3. //直接从节点属性中取
  4. private Integer fetchSize;
  5. //直接从节点属性中取
  6. private Integer timeout;
  7. private StatementType statementType;
  8. private ResultSetType resultSetType;
  9. //对应一条SQL语句
  10. private SqlSource sqlSource;
  11. //每条语句都对就一个缓存,如果有的话。
  12. private Cache cache;
  13. //这个已经过时了
  14. private ParameterMap parameterMap;
  15. private List<ResultMap> resultMaps;
  16. private boolean flushCacheRequired;
  17. private boolean useCache;
  18. private boolean resultOrdered;
  19. //SQL的类型,select/update/insert/detete
  20. private SqlCommandType sqlCommandType;
  21. private KeyGenerator keyGenerator;
  22. private String[] keyProperties;
  23. private String[] keyColumns;
  24. //是否有内映射
  25. private boolean hasNestedResultMaps;
  26. private String databaseId;
  27. private Log statementLog;
  28. private LanguageDriver lang;
  29. private String[] resultSets;

上面属性都比较简单,复杂的是SqlSource,下面有详细的描述!

XMLStatementBuilder.parseStatementNode()方法

resultMap元素的解析已经分析完毕。与resultMap不一样,XmlMapperBuilder在解析select/update /insert/delete的元素时会创建一个XMLStatementBuilder对象,解析的工作交由其方法 parseStatementNode()方法完成。

[java] view plain copy
  1. private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  2. for (XNode context : list) {
  3. //一个select/update/insert/delete元素创建一个XMLStatementBuilder对象
  4. final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
  5. try {
  6. //将元素解析成MappedStatemenet对象,并加入到Configuration中去
  7. statementParser.parseStatementNode();
  8. } catch (IncompleteElementException e) {
  9. configuration.addIncompleteStatement(statementParser);
  10. }
  11. }

如下是parseStatementNode()方法的代码

[java] view plain copy
  1. public void parseStatementNode() {
  2. String id = context.getStringAttribute("id");
  3. String databaseId = context.getStringAttribute("databaseId");
  4. if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
  5. Integer fetchSize = context.getIntAttribute("fetchSize");
  6. Integer timeout = context.getIntAttribute("timeout");
  7. String parameterMap = context.getStringAttribute("parameterMap");
  8. String parameterType = context.getStringAttribute("parameterType");
  9. Class<?> parameterTypeClass = resolveClass(parameterType);
  10. String resultMap = context.getStringAttribute("resultMap");
  11. String resultType = context.getStringAttribute("resultType");
  12. String lang = context.getStringAttribute("lang");
  13. LanguageDriver langDriver = getLanguageDriver(lang);
  14. Class<?> resultTypeClass = resolveClass(resultType);
  15. String resultSetType = context.getStringAttribute("resultSetType");
  16. //Statement的类型,对应jdbc里的三个类型:Statement、PreparedStatement、CallableStatement,默认使用PreparedStatement
  17. StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  18. //这个也是跟jdbc里相对应的,一般采用默认即可
  19. ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  20. //Sql的类型,select/update/insert/delete
  21. String nodeName = context.getNode().getNodeName();
  22. SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  23. boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  24. //是否刷新缓存
  25. boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  26. //是否使用缓存
  27. boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  28. boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
  29. //不做分析
  30. // Include Fragments before parsing
  31. XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  32. includeParser.applyIncludes(context.getNode());
  33. //不做分析
  34. // Parse selectKey after includes and remove them.
  35. processSelectKeyNodes(id, parameterTypeClass, langDriver);
  36. //生成SqlSource对象,这个对象非常重要,接下来详细分析
  37. // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  38. SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  39. String resultSets = context.getStringAttribute("resultSets");
  40. String keyProperty = context.getStringAttribute("keyProperty");
  41. String keyColumn = context.getStringAttribute("keyColumn");
  42. //自动生成key,这里也不做讨论
  43. KeyGenerator keyGenerator;
  44. String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  45. keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  46. if (configuration.hasKeyGenerator(keyStatementId)) {
  47. keyGenerator = configuration.getKeyGenerator(keyStatementId);
  48. } else {
  49. keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
  50. configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
  51. ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
  52. }
  53. //生成MappedStatement对象,并加到Configuration中
  54. builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
  55. fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
  56. resultSetTypeEnum, flushCache, useCache, resultOrdered,
  57. keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  58. }

上在方法里附件解析一些基本的属性外还有两个主要的部分

  1. SqlSource的构建过程

    [java] view plain copy
    1. SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  2. MappedStatement的构建过程
    [java] view plain copy
    1. builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
    2. fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
    3. resultSetTypeEnum, flushCache, useCache, resultOrdered,
    4. keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

SqlSource构建过程

SqlSource接口

[java] view plain copy
  1. /**
  2. * Represents the content of a mapped statement read from an XML file or an annotation.
  3. * It creates the SQL that will be passed to the database out of the input parameter received from the user.
  4. *
  5. * @author Clinton Begin
  6. */
  7. public interface SqlSource {
  8. BoundSql getBoundSql(Object parameterObject);
  9. }

SqlSource表示从mapper.xml或注解中读取的sql内容,该sql一般还不能都被直接执行,例如

[html] view plain copy
  1. <select id="selectUserDetail" resultMap="detailUserResultMap">
  2. <!--CDATA里内容会都解析成一个SqlSource对象-->
  3. <![CDATA[
  4. select user_id,user_name,user_type,cust_id from tf_f_user a where a.user_id=#${userId}
  5. ]]>/select>

SqlSource只有一个方法:getBoundSql(paramenterObject),其中paramenterObject为运行sql里的实际参数

BoundSql

[java] view plain copy
  1. /**
  2. * An actual SQL String got form an {@link SqlSource} after having processed any dynamic content.
  3. * The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings
  4. * with the additional information for each parameter (at least the property name of the input object to read
  5. * the value from).
  6. * </br>
  7. * Can also have additional parameters that are created by the dynamic language (for loops, bind...).
  8. */
  9. /**
  10. * @author Clinton Begin
  11. */
  12. public class BoundSql {
  13. private String sql;
  14. private List<ParameterMapping> parameterMappings;
  15. private Object parameterObject;
  16. private Map<String, Object> additionalParameters;
  17. private MetaObject metaParameters;
  18. public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
  19. this.sql = sql;
  20. this.parameterMappings = parameterMappings;
  21. this.parameterObject = parameterObject;
  22. this.additionalParameters = new HashMap<String, Object>();
  23. this.metaParameters = configuration.newMetaObject(additionalParameters);
  24. }
  25. public String getSql() {
  26. return sql;
  27. }
  28. public List<ParameterMapping> getParameterMappings() {
  29. return parameterMappings;
  30. }
  31. public Object getParameterObject() {
  32. return parameterObject;
  33. }
  34. public boolean hasAdditionalParameter(String name) {
  35. return metaParameters.hasGetter(name);
  36. }
  37. public void setAdditionalParameter(String name, Object value) {
  38. metaParameters.setValue(name, value);
  39. }
  40. public Object getAdditionalParameter(String name) {
  41. return metaParameters.getValue(name);
  42. }
  43. }

SqlBound代码并不多,就是一个普通的java对象,有两个属性非常重要

  1. sql:看代码里的注解,这个sql已经是经过了一些处理,可以被jdbc执行的了。xml里配置的sql可能有占位符#{username},这里的sql占位符已经被替换成"?"号了。
  2. parameterMappings:执行sql对象的实际的参数。由此可以判断,每执行一条sql都会创建一个BoundSql对象。
SqlSource和BoundSql本身并不复杂,复杂的是这两个对象被创建的过程。

LanguageDriver

SqlSource对象是通过LanguageDriver对象构建的,在mapper.xml配置sql里可以通过lang属性指定一个 LanguageDriver,但我们通常不会这样子做。当lang属性没有配置时,Mybatis会属性默认给一个。这个默认的 LanguageDriver在Configuration的构造方法中定义的:

[java] view plain copy
  1. public Configuration() {
  2. ...
  3. languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  4. languageRegistry.register(RawLanguageDriver.class);
  5. }

马上来看XMLLanguageDriver.createSqlSource()方法
[java] view plain copy
  1. public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
  2. XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
  3. return builder.parseScriptNode();
  4. }

XMLScriptBuilder

XMLScriptBuilder.parseScriptNode()方法

[java] view plain copy
  1. public SqlSource parseScriptNode() {
  2. //将一个sql内容解析成多个SqlNode
  3. List<SqlNode> contents = parseDynamicTags(context);
  4. //将多个SqlNode组合一个SqlNode
  5. MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
  6. SqlSource sqlSource = null;
  7. //判断sql是否是动态的
  8. if (isDynamic) {
  9. //生成动态的SqlSource
  10. sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
  11. } else {
  12. //生成静态的SqlSource
  13. sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
  14. }
  15. return sqlSource;
  16. }

再看parseDynamicTagS(context)方法

[java] view plain copy
  1. private List<SqlNode> parseDynamicTags(XNode node) {
  2. //一个sql会被解析成多个SqlNode,稍后会有示例详细说明
  3. List<SqlNode> contents = new ArrayList<SqlNode>();
  4. NodeList children = node.getNode().getChildNodes();
  5. for (int i = 0; i < children.getLength(); i++) {
  6. XNode child = node.newXNode(children.item(i));
  7. if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
  8. //如果这个Node只包含文本
  9. String data = child.getStringBody("");
  10. //生成一个TextSqlNode
  11. TextSqlNode textSqlNode = new TextSqlNode(data);
  12. //判断是否是动态的,如果文本里包含占位符,如#{username}或{table_name},isDynamic()方法就会返回true
  13. if (textSqlNode.isDynamic()) {
  14. contents.add(textSqlNode);
  15. isDynamic = true;
  16. } else {
  17. contents.add(new StaticTextSqlNode(data));
  18. }
  19. } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
  20. //如果是有xml标签的Node,交由Handler处理,同时被认为是动态的
  21. String nodeName = child.getNode().getNodeName();
  22. NodeHandler handler = nodeHandlers.get(nodeName);
  23. if (handler == null) {
  24. throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
  25. }
  26. handler.handleNode(child, contents);
  27. isDynamic = true;
  28. }
  29. }
  30. return contents;
  31. }

再看看nodeHandlers都有那些

[java] view plain copy
  1. private Map<String, NodeHandler> nodeHandlers = new HashMap<String, NodeHandler>() {
  2. private static final long serialVersionUID = 7123056019193266281L;
  3. {
  4. //Mybatis3动态sql都支持那些配置,这里就很清楚啦
  5. put("trim", new TrimHandler());
  6. put("where", new WhereHandler());
  7. put("set", new SetHandler());
  8. put("foreach", new ForEachHandler());
  9. put("if", new IfHandler());
  10. put("choose", new ChooseHandler());
  11. put("when", new IfHandler());
  12. put("otherwise", new OtherwiseHandler());
  13. put("bind", new BindHandler());
  14. }
  15. };

看到这里基本上能了解sql是怎么被解析的啦!举例说明:

[html] view plain copy
  1. <select id="selectUserDetail" resultMap="detailUserResultMap">
  2. <![CDATA[
  3. select user_id,user_name,user_type,cust_id --这里一行会解析成一个StaticTextSqlNode
  4. from tf_f_user a --这里一行也会解析成一个StaticTextSqlNode
  5. where a.user_id=#{userId} --这行会被解析成TextSqlNode,并且isDynamic被设置成true,因为有占位符
  6. --这个空行也解析成一个StaticTextSqlNode
  7. ]]><!-- 这四个SqlNode会被组合成一个MixedSqlNode -->
  8. </select>

再来个动态sql的:

[html] view plain copy
  1. <select id="selectUserDetail" resultMap="detailUserResultMap">
  2. <![CDATA[
  3. select user_id,user_name,user_type,cust_id --这里一行会解析成一个StaticTextSqlNode
  4. from tf_f_user a --这里一行也会解析成一个StaticTextSqlNode
  5. where a.user_id=#{userId} --这行会被解析成TextSqlNode,并且isDynamic被设置成true,因为有占位符
  6. --这个空行也解析成一个StaticTextSqlNode
  7. ]]>
  8. <if test="user_name!=null"> <!-- 这个标签里的内容会交给IfHandler处理 -->
  9. and --这里的解析与上行的一样,解析成一个StaticTextSqlNode
  10. user_name=#{userName} --这里的解析与上行的一样,也会被解析成一个TextSqlNode,并且isDynamic被设置成true,因为有占位符
  11. </if><!-- IfHandler会将这里面的内个SqlNode组成MixedSqlNode再组成一个IfSqlNode -->
  12. </select><!-- 这五个SqlNode会被组合成一个MixedSqlNode -->

附上IfHandler的代码

[java] view plain copy
  1. private class IfHandler implements NodeHandler {
  2. public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
  3. //解析子节点
  4. List<SqlNode> contents = parseDynamicTags(nodeToHandle);
  5. //组合
  6. MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
  7. String test = nodeToHandle.getStringAttribute("test");
  8. //生成IfSqlNode
  9. IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
  10. targetContents.add(ifSqlNode);
  11. }
  12. }

其他的nodeHandler在这里就不讨论了,实现方式与IfHandler差不多。如下两个方法也不在这里做讨论

  1. SqlSource.getBoundSql()方法
  2. SqlNode.apply(DynamicContextcontext)方法

http://blog.csdn.net/ashan_li/article/details/50351080

转载于:https://my.oschina.net/xiaominmin/blog/1599100

Mybatis3源码分析(05)-加载Configuration-加载MappedStatement相关推荐

  1. Mybatis 源码分析(一)配置文件加载流程

    Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...

  2. 【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 ...

  3. Tomcat源码分析——server.xml文件的加载

    前言 作为Java程序员,对于tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载的进行分析. 源码分析 Bootstrap的 ...

  4. thinkphp源码分析(三)—自动加载篇(Loader的分析)

    源码分析 自动加载 系统会调用 Loader::register()方法注册自动加载,在这一步完成后,所有符合规范的类库(包括Composer依赖加载的第三方类库)都将自动加载. 系统的自动加载由下面 ...

  5. Tomcat7.0源码分析——server.xml文件的加载与解析

    前言 作为Java程序员,对于Tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载和解析进行分析. 加载过程分析 Bootst ...

  6. SpringMVC源码分析_1 SpringMVC容器启动和加载原理

                                                                    SpringMVC源码分析_1 SpringMVC启动和加载原理     ...

  7. 云客Drupal源码分析之配置系统Configuration(一)

    各位<云客drupal源码分析>系列的读者: 本系列一直以每周一篇的速度进行博客原创更新,希望帮助大家理解drupal8底层原理,并缩短学习时间,但自<插件系统(上)>主题开始 ...

  8. flume源码分析2--配置文件的加载

    上面提到Application启动的时候,PollingPropertiesFileConfigurationProvider作为唯一的LifecycleAware类型的组件被交给监护者Lifecyc ...

  9. llvm libLLVMCore源码分析 05 - Instruction Class

    源码路径 llvm\include\llvm\IR\Instruction.h llvm\include\llvm\IR\Instruction.def llvm\include\llvm\IR\In ...

最新文章

  1. python可以做什么工作好-学Python能找到什么工作?这4种工作最热门!
  2. vmware上给根分区增加空间以及创建逻辑卷
  3. 以太坊知识教程------交易
  4. docker18.09.3修改守护进程配置文件不生效问题
  5. Spark _29_SparkStreaming初始
  6. python爬虫网络请求超时是什么意思_python爬虫怎么处理异常和超时?
  7. Spring Security登录
  8. IMPDP导入实例(oracle)
  9. java treeset比较,java中TreeSet的两种排序比较的方式
  10. Hadoop平台搭建
  11. python 邻接矩阵_阿里巴巴举荐,Python视频,免费分享,用python求解特征向量和拉普拉斯矩阵...
  12. js做四则运算时,精度丢失问题及解决方法
  13. tcp长连接java_聊聊 TCP 长连接和心跳那些事
  14. mysql linux查看配置文件my.cnf位置
  15. 【三维路径规划】基于matlab A_star算法无人机三维路径规划【含Matlab源码 1387期】
  16. js html 导出excel文件,js 导出excel ,elsx格式
  17. 基于Springboot的Java邮件系统的设计与实现(附论文和源码)
  18. win10激活方法(无需下载)
  19. 蓝牙核心规范(V5.2)3.2-深入详解之数据传输架构
  20. 联合国首席AI顾问专访:我们期望AI应该是完美的,但这永远不会

热门文章

  1. 安装Adventure Works 2008 R2演示数据库
  2. 在织梦模板中不适用ajax,直接用标签也能调用当前会员的信息
  3. 华人“军火专家”——黄仁勋 1
  4. 微信小程序教程 [第五篇]脚本语言(WXS)
  5. 戴尔:发力互联互通 构建世界基础设施中心
  6. WEB中会话跟踪[转]
  7. BZOJ 3224: Tyvj 1728 普通平衡树 treap
  8. MS CRM 2011 RC中的新特性(2)——销售自动化方面
  9. VLAN系列二: 实现VLAN的机制
  10. asp.net(c#) linkbutton CommandArgument