Mybatis3源码分析(05)-加载Configuration-加载MappedStatement

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. //每条语句都对就一个缓存,如果有的话。  
  13. private Cache cache;  
  14. //这个已经过时了  
  15. private ParameterMap parameterMap;  
  16. private List<ResultMap> resultMaps;  
  17. private boolean flushCacheRequired;  
  18. private boolean useCache;  
  19. private boolean resultOrdered;  
  20. //SQL的类型,select/update/insert/detete  
  21. private SqlCommandType sqlCommandType;  
  22. private KeyGenerator keyGenerator;  
  23. private String[] keyProperties;  
  24. private String[] keyColumns;  
  25.   
  26. //是否有内映射  
  27. private boolean hasNestedResultMaps;  
  28. private String databaseId;  
  29. private Log statementLog;  
  30. private LanguageDriver lang;  
  31. 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.   
  5.     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;  
  6.   
  7.     Integer fetchSize = context.getIntAttribute("fetchSize");  
  8.     Integer timeout = context.getIntAttribute("timeout");  
  9.     String parameterMap = context.getStringAttribute("parameterMap");  
  10.     String parameterType = context.getStringAttribute("parameterType");  
  11.     Class<?> parameterTypeClass = resolveClass(parameterType);  
  12.     String resultMap = context.getStringAttribute("resultMap");  
  13.     String resultType = context.getStringAttribute("resultType");  
  14.     String lang = context.getStringAttribute("lang");  
  15.     LanguageDriver langDriver = getLanguageDriver(lang);  
  16.   
  17.     Class<?> resultTypeClass = resolveClass(resultType);  
  18.     String resultSetType = context.getStringAttribute("resultSetType");  
  19.     //Statement的类型,对应jdbc里的三个类型:Statement、PreparedStatement、CallableStatement,默认使用PreparedStatement  
  20.     StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));  
  21.     //这个也是跟jdbc里相对应的,一般采用默认即可  
  22.     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);  
  23.      
  24.     //Sql的类型,select/update/insert/delete  
  25.     String nodeName = context.getNode().getNodeName();  
  26.     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));  
  27.     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;  
  28.     //是否刷新缓存  
  29.     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);  
  30.     //是否使用缓存  
  31.     boolean useCache = context.getBooleanAttribute("useCache", isSelect);  
  32.     boolean resultOrdered = context.getBooleanAttribute("resultOrdered"false);  
  33.   
  34.     //不做分析  
  35.     // Include Fragments before parsing  
  36.     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);  
  37.     includeParser.applyIncludes(context.getNode());  
  38.     //不做分析  
  39.     // Parse selectKey after includes and remove them.  
  40.     processSelectKeyNodes(id, parameterTypeClass, langDriver);  
  41.       
  42.     //生成SqlSource对象,这个对象非常重要,接下来详细分析  
  43.     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)  
  44.     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);  
  45.     String resultSets = context.getStringAttribute("resultSets");  
  46.     String keyProperty = context.getStringAttribute("keyProperty");  
  47.     String keyColumn = context.getStringAttribute("keyColumn");  
  48.       
  49.     //自动生成key,这里也不做讨论  
  50.     KeyGenerator keyGenerator;  
  51.     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;  
  52.     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);  
  53.     if (configuration.hasKeyGenerator(keyStatementId)) {  
  54.       keyGenerator = configuration.getKeyGenerator(keyStatementId);  
  55.     } else {  
  56.       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",  
  57.           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))  
  58.           ? new Jdbc3KeyGenerator() : new NoKeyGenerator();  
  59.     }  
  60.     //生成MappedStatement对象,并加到Configuration中  
  61.     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,  
  62.         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,  
  63.         resultSetTypeEnum, flushCache, useCache, resultOrdered,   
  64.         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);  
  65.   }  
上在方法里附件解析一些基本的属性外还有两个主要的部分
  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.   
  9.   BoundSql getBoundSql(Object parameterObject);  
  10.   
  11. }  
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.   
  14.   private String sql;  
  15.   private List<ParameterMapping> parameterMappings;  
  16.   private Object parameterObject;  
  17.   private Map<String, Object> additionalParameters;  
  18.   private MetaObject metaParameters;  
  19.   
  20.   public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {  
  21.     this.sql = sql;  
  22.     this.parameterMappings = parameterMappings;  
  23.     this.parameterObject = parameterObject;  
  24.     this.additionalParameters = new HashMap<String, Object>();  
  25.     this.metaParameters = configuration.newMetaObject(additionalParameters);  
  26.   }  
  27.   
  28.   public String getSql() {  
  29.     return sql;  
  30.   }  
  31.   
  32.   public List<ParameterMapping> getParameterMappings() {  
  33.     return parameterMappings;  
  34.   }  
  35.   
  36.   public Object getParameterObject() {  
  37.     return parameterObject;  
  38.   }  
  39.   
  40.   public boolean hasAdditionalParameter(String name) {  
  41.     return metaParameters.hasGetter(name);  
  42.   }  
  43.   
  44.   public void setAdditionalParameter(String name, Object value) {  
  45.     metaParameters.setValue(name, value);  
  46.   }   
  47.   
  48.   public Object getAdditionalParameter(String name) {  
  49.     return metaParameters.getValue(name);   
  50.   }  
  51. }  
  52.       
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.       
  28.         isDynamic = true;  
  29.       }  
  30.     }  
  31.     return contents;  
  32.   }  

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

看到这里基本上能了解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

猜你喜欢

转载自m635674608.iteye.com/blog/2312975
今日推荐