XMLStatementBuilder.java

  1. /*
  2.  *    Copyright 2009-2024 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.builder.xml;

  17. import java.util.List;
  18. import java.util.Locale;

  19. import org.apache.ibatis.builder.BaseBuilder;
  20. import org.apache.ibatis.builder.MapperBuilderAssistant;
  21. import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
  22. import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
  23. import org.apache.ibatis.executor.keygen.KeyGenerator;
  24. import org.apache.ibatis.executor.keygen.NoKeyGenerator;
  25. import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
  26. import org.apache.ibatis.mapping.MappedStatement;
  27. import org.apache.ibatis.mapping.ResultSetType;
  28. import org.apache.ibatis.mapping.SqlCommandType;
  29. import org.apache.ibatis.mapping.SqlSource;
  30. import org.apache.ibatis.mapping.StatementType;
  31. import org.apache.ibatis.parsing.XNode;
  32. import org.apache.ibatis.scripting.LanguageDriver;
  33. import org.apache.ibatis.session.Configuration;

  34. /**
  35.  * @author Clinton Begin
  36.  */
  37. public class XMLStatementBuilder extends BaseBuilder {

  38.   private final MapperBuilderAssistant builderAssistant;
  39.   private final XNode context;
  40.   private final String requiredDatabaseId;

  41.   public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context) {
  42.     this(configuration, builderAssistant, context, null);
  43.   }

  44.   public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context,
  45.       String databaseId) {
  46.     super(configuration);
  47.     this.builderAssistant = builderAssistant;
  48.     this.context = context;
  49.     this.requiredDatabaseId = databaseId;
  50.   }

  51.   public void parseStatementNode() {
  52.     String id = context.getStringAttribute("id");
  53.     String databaseId = context.getStringAttribute("databaseId");

  54.     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
  55.       return;
  56.     }

  57.     String nodeName = context.getNode().getNodeName();
  58.     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  59.     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  60.     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  61.     boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  62.     boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  63.     // Include Fragments before parsing
  64.     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  65.     includeParser.applyIncludes(context.getNode());

  66.     String parameterType = context.getStringAttribute("parameterType");
  67.     Class<?> parameterTypeClass = resolveClass(parameterType);

  68.     String lang = context.getStringAttribute("lang");
  69.     LanguageDriver langDriver = getLanguageDriver(lang);

  70.     // Parse selectKey after includes and remove them.
  71.     processSelectKeyNodes(id, parameterTypeClass, langDriver);

  72.     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  73.     KeyGenerator keyGenerator;
  74.     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  75.     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  76.     if (configuration.hasKeyGenerator(keyStatementId)) {
  77.       keyGenerator = configuration.getKeyGenerator(keyStatementId);
  78.     } else {
  79.       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
  80.           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
  81.               ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  82.     }

  83.     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  84.     StatementType statementType = StatementType
  85.         .valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  86.     Integer fetchSize = context.getIntAttribute("fetchSize");
  87.     Integer timeout = context.getIntAttribute("timeout");
  88.     String parameterMap = context.getStringAttribute("parameterMap");
  89.     String resultType = context.getStringAttribute("resultType");
  90.     Class<?> resultTypeClass = resolveClass(resultType);
  91.     String resultMap = context.getStringAttribute("resultMap");
  92.     if (resultTypeClass == null && resultMap == null) {
  93.       resultTypeClass = MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id);
  94.     }
  95.     String resultSetType = context.getStringAttribute("resultSetType");
  96.     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  97.     if (resultSetTypeEnum == null) {
  98.       resultSetTypeEnum = configuration.getDefaultResultSetType();
  99.     }
  100.     String keyProperty = context.getStringAttribute("keyProperty");
  101.     String keyColumn = context.getStringAttribute("keyColumn");
  102.     String resultSets = context.getStringAttribute("resultSets");
  103.     boolean dirtySelect = context.getBooleanAttribute("affectData", Boolean.FALSE);

  104.     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
  105.         parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
  106.         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);
  107.   }

  108.   private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
  109.     List<XNode> selectKeyNodes = context.evalNodes("selectKey");
  110.     if (configuration.getDatabaseId() != null) {
  111.       parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
  112.     }
  113.     parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
  114.     removeSelectKeyNodes(selectKeyNodes);
  115.   }

  116.   private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass,
  117.       LanguageDriver langDriver, String skRequiredDatabaseId) {
  118.     for (XNode nodeToHandle : list) {
  119.       String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  120.       String databaseId = nodeToHandle.getStringAttribute("databaseId");
  121.       if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
  122.         parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
  123.       }
  124.     }
  125.   }

  126.   private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver,
  127.       String databaseId) {
  128.     String resultType = nodeToHandle.getStringAttribute("resultType");
  129.     Class<?> resultTypeClass = resolveClass(resultType);
  130.     StatementType statementType = StatementType
  131.         .valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  132.     String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
  133.     String keyColumn = nodeToHandle.getStringAttribute("keyColumn");
  134.     boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));

  135.     // defaults
  136.     boolean useCache = false;
  137.     boolean resultOrdered = false;
  138.     KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
  139.     Integer fetchSize = null;
  140.     Integer timeout = null;
  141.     boolean flushCache = false;
  142.     String parameterMap = null;
  143.     String resultMap = null;
  144.     ResultSetType resultSetTypeEnum = null;

  145.     SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
  146.     SqlCommandType sqlCommandType = SqlCommandType.SELECT;

  147.     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
  148.         parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
  149.         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null, false);

  150.     id = builderAssistant.applyCurrentNamespace(id, false);

  151.     MappedStatement keyStatement = configuration.getMappedStatement(id, false);
  152.     configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
  153.   }

  154.   private void removeSelectKeyNodes(List<XNode> selectKeyNodes) {
  155.     for (XNode nodeToHandle : selectKeyNodes) {
  156.       nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
  157.     }
  158.   }

  159.   private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
  160.     if (requiredDatabaseId != null) {
  161.       return requiredDatabaseId.equals(databaseId);
  162.     }
  163.     if (databaseId != null) {
  164.       return false;
  165.     }
  166.     id = builderAssistant.applyCurrentNamespace(id, false);
  167.     if (!this.configuration.hasStatement(id, false)) {
  168.       return true;
  169.     }
  170.     // skip this statement if there is a previous one with a not null databaseId
  171.     MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
  172.     return previous.getDatabaseId() == null;
  173.   }

  174.   private LanguageDriver getLanguageDriver(String lang) {
  175.     Class<? extends LanguageDriver> langClass = null;
  176.     if (lang != null) {
  177.       langClass = resolveClass(lang);
  178.     }
  179.     return configuration.getLanguageDriver(langClass);
  180.   }

  181. }