MappedStatement说明
一个MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条SQL语句。其属性有
- //节点中的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()方法
resultMap元素的解析已经分析完毕。与resultMap不一样,XmlMapperBuilder在解析select/update /insert/delete的元素时会创建一个XMLStatementBuilder对象,解析的工作交由其方法 parseStatementNode()方法完成。
如下是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);
- }
- }
- 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的构建过程
- SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
- MappedStatement的构建过程
- 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);
- }
- <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>
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);
- }
- }
- sql:看代码里的注解,这个sql已经是经过了一些处理,可以被jdbc执行的了。xml里配置的sql可能有占位符#{username},这里的sql占位符已经被替换成"?"号了。
- parameterMappings:执行sql对象的实际的参数。由此可以判断,每执行一条sql都会创建一个BoundSql对象。
SqlSource和BoundSql本身并不复杂,复杂的是这两个对象被创建的过程。
LanguageDriver
SqlSource对象是通过LanguageDriver对象构建的,在mapper.xml配置sql里可以通过lang属性指定一个 LanguageDriver,但我们通常不会这样子做。当lang属性没有配置时,Mybatis会属性默认给一个。这个默认的 LanguageDriver在Configuration的构造方法中定义的:
- public Configuration() {
- ...
- languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
- languageRegistry.register(RawLanguageDriver.class);
- }
马上来看XMLLanguageDriver.createSqlSource()方法
- 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
相关推荐
赠送jar包:mybatis-plus-boot-starter-3.5.1.jar; 赠送原API文档:mybatis-plus-boot-starter-3.5.1-javadoc.jar; 赠送源代码:mybatis-plus-boot-starter-3.5.1-sources.jar; 赠送Maven依赖信息文件:mybatis-...
赠送jar包:mybatis-plus-extension-3.5.1.jar; 赠送原API文档:mybatis-plus-extension-3.5.1-javadoc.jar; 赠送源代码:mybatis-plus-extension-3.5.1-sources.jar; 赠送Maven依赖信息文件:mybatis-plus-...
本工程为 MyBatis-Plus 的官方示例,项目结构如下: mybatis-plus-sample-quickstart: 快速开始示例 mybatis-plus-sample-quickstart-springmvc: 快速开始...mybatis-plus-sample-execution-analysis: Sql执行分析示例
赠送jar包:mybatis-spring-boot-autoconfigure-1.3.2.jar; 赠送原API文档:mybatis-spring-boot-autoconfigure-1.3.2-javadoc.jar; 赠送源代码:mybatis-spring-boot-autoconfigure-1.3.2-sources.jar; 赠送...
1. **配置解析**:MyBatis的配置文件(mybatis-config.xml)被解析成Configration对象,这个对象包含了所有的全局配置信息,如数据源、事务管理器、Mappers等。解析过程主要由XMLConfigBuilder类完成。 2. **...
mybatis-plus-boot-starter.jar 各个版本下载, SpringBoot 集成 MybatisPlus jar 包下载, Mybatis-Plus(简称MP)是一个基于MyBatis的增强工具库,它简化了与数据库的交互操作并提供了一系列增强功能,使开发者...
MyBatis 是一款著名的持久层框架...通过对 MyBatis-3.5.9 源码的阅读和分析,我们可以深入了解其实现原理,为日常开发和性能优化提供有力支持。同时,这也有助于我们更好地理解和定制 MyBatis,使其更加符合项目需求。
mybatis-spring-boot-starter-2.1.4.jarmybatis-spring-boot-starter-2.1.4.jar
mybatis-plus-boot-starter.jar 各个版本下载, SpringBoot 集成 MybatisPlus jar 包下载, Mybatis-Plus(简称MP)是一个基于MyBatis的增强工具库,它简化了与数据库的交互操作并提供了一系列增强功能,使开发者...
mybatis-spring-boot-starter-2.1.3.jarmybatis-spring-boot-starter-2.1.3.jarmybatis-spring-boot-starter-2.1.3.jar
mybatis-spring-boot-autoconfigure-2.1.3mybatis-spring-boot-autoconfigure-2.1.3
mybatis mybatis-spring-boot-starter-2.0.0.jar下载
mybatis-plus-boot-starter.jar 各个版本下载, SpringBoot 集成 MybatisPlus jar 包下载, Mybatis-Plus(简称MP)是一个基于MyBatis的增强工具库,它简化了与数据库的交互操作并提供了一系列增强功能,使开发者...
mybatis-generator-core-1.3.2(mybatis自动生产中文注释-解决没有主清单属性的问题)
赠送jar包:mybatis-spring-boot-autoconfigure-2.1.1.jar; 赠送原API文档:mybatis-spring-boot-autoconfigure-2.1.1-javadoc.jar; 赠送源代码:mybatis-spring-boot-autoconfigure-2.1.1-sources.jar; 赠送...
mybatis-plus-boot-starter.jar 各个版本下载, SpringBoot 集成 MybatisPlus jar 包下载, Mybatis-Plus(简称MP)是一个基于MyBatis的增强工具库,它简化了与数据库的交互操作并提供了一系列增强功能,使开发者...
mybatis-plus-boot-starter.jar 各个版本下载, SpringBoot 集成 MybatisPlus jar 包下载, Mybatis-Plus(简称MP)是一个基于MyBatis的增强工具库,它简化了与数据库的交互操作并提供了一系列增强功能,使开发者...
赠送jar包:mybatis-spring-boot-autoconfigure-1.3.2.jar; 赠送原API文档:mybatis-spring-boot-autoconfigure-1.3.2-javadoc.jar; 赠送源代码:mybatis-spring-boot-autoconfigure-1.3.2-sources.jar; 赠送...
"mybatis-generator-core-1.3.2" 是 MBG 的一个特定版本,它包含了所有必要的组件来帮助我们自动化 MyBatis 配置和代码生成过程。 MyBatis Generator 使用 XML 配置文件来定义生成规则,包括数据库连接信息、表选择...
赠送jar包:mybatis-generator-core-1.3.7.jar; 赠送原API文档:mybatis-generator-core-1.3.7-javadoc.jar; 赠送源代码:mybatis-generator-core-1.3.7-sources.jar; 赠送Maven依赖信息文件:mybatis-generator-...