• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / mybatis-3 / 2686

01 Feb 2025 09:55PM UTC coverage: 87.093% (-0.1%) from 87.217%
2686

Pull #3379

github

web-flow
Merge c97c5c598 into 3d71c862a
Pull Request #3379: Resolve type handler based on `java.lang.reflect.Type` instead of `Class` and respect runtime JDBC type

3825 of 4663 branches covered (82.03%)

515 of 579 new or added lines in 36 files covered. (88.95%)

28 existing lines in 6 files now uncovered.

9912 of 11381 relevant lines covered (87.09%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.69
/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java
1
/*
2
 *    Copyright 2009-2025 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

18
import java.lang.reflect.Method;
19
import java.lang.reflect.Type;
20
import java.util.Arrays;
21
import java.util.List;
22
import java.util.Locale;
23
import java.util.stream.Collectors;
24

25
import org.apache.ibatis.binding.MapperMethod.ParamMap;
26
import org.apache.ibatis.builder.BaseBuilder;
27
import org.apache.ibatis.builder.MapperBuilderAssistant;
28
import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
29
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
30
import org.apache.ibatis.executor.keygen.KeyGenerator;
31
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
32
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
33
import org.apache.ibatis.mapping.MappedStatement;
34
import org.apache.ibatis.mapping.ResultSetType;
35
import org.apache.ibatis.mapping.SqlCommandType;
36
import org.apache.ibatis.mapping.SqlSource;
37
import org.apache.ibatis.mapping.StatementType;
38
import org.apache.ibatis.parsing.XNode;
39
import org.apache.ibatis.reflection.ParamNameResolver;
40
import org.apache.ibatis.scripting.LanguageDriver;
41
import org.apache.ibatis.session.Configuration;
42

43
/**
44
 * @author Clinton Begin
45
 */
46
public class XMLStatementBuilder extends BaseBuilder {
47

48
  private final MapperBuilderAssistant builderAssistant;
49
  private final XNode context;
50
  private final String requiredDatabaseId;
51
  private final Class<?> mapperClass;
52

53
  public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context) {
54
    this(configuration, builderAssistant, context, null);
×
55
  }
×
56

57
  public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context,
58
      String databaseId) {
NEW
59
    this(configuration, builderAssistant, context, null, null);
×
NEW
60
  }
×
61

62
  public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context,
63
      String databaseId, Class<?> mapperClass) {
64
    super(configuration);
1✔
65
    this.builderAssistant = builderAssistant;
1✔
66
    this.context = context;
1✔
67
    this.requiredDatabaseId = databaseId;
1✔
68
    this.mapperClass = mapperClass;
1✔
69
  }
1✔
70

71
  public void parseStatementNode() {
72
    String id = context.getStringAttribute("id");
1✔
73
    String databaseId = context.getStringAttribute("databaseId");
1✔
74

75
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
1✔
76
      return;
1✔
77
    }
78

79
    String nodeName = context.getNode().getNodeName();
1✔
80
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
1✔
81
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
1✔
82
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
1✔
83
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
1✔
84
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
1✔
85

86
    // Include Fragments before parsing
87
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
1✔
88
    includeParser.applyIncludes(context.getNode());
1✔
89

90
    String parameterType = context.getStringAttribute("parameterType");
1✔
91
    Class<?> parameterTypeClass = resolveClass(parameterType);
1✔
92
    ParamNameResolver paramNameResolver = null;
1✔
93
    if (parameterTypeClass == null && mapperClass != null) {
1✔
94
      List<Method> mapperMethods = Arrays.stream(mapperClass.getMethods())
1✔
95
          .filter(m -> m.getName().equals(id) && !m.isDefault() && !m.isBridge()).collect(Collectors.toList());
1!
96
      if (mapperMethods.size() == 1) {
1✔
97
        paramNameResolver = new ParamNameResolver(configuration, mapperMethods.get(0), mapperClass);
1✔
98
        if (paramNameResolver.isUseParamMap()) {
1✔
99
          parameterTypeClass = ParamMap.class;
1✔
100
        } else {
101
          String[] paramNames = paramNameResolver.getNames();
1✔
102
          if (paramNames.length == 1) {
1✔
103
            Type paramType = paramNameResolver.getType(paramNames[0]);
1✔
104
            if (paramType instanceof Class) {
1✔
105
              parameterTypeClass = (Class<?>) paramType;
1✔
106
            }
107
          }
108
        }
109
      }
110
    }
111

112
    String lang = context.getStringAttribute("lang");
1✔
113
    LanguageDriver langDriver = getLanguageDriver(lang);
1✔
114

115
    // Parse selectKey after includes and remove them.
116
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
1✔
117

118
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
119
    KeyGenerator keyGenerator;
120
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
1✔
121
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
1✔
122
    if (configuration.hasKeyGenerator(keyStatementId)) {
1✔
123
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
1✔
124
    } else {
125
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
1✔
126
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
1!
127
              ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
1✔
128
    }
129

130
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass, paramNameResolver);
1✔
131
    StatementType statementType = StatementType
1✔
132
        .valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
1✔
133
    Integer fetchSize = context.getIntAttribute("fetchSize");
1✔
134
    Integer timeout = context.getIntAttribute("timeout");
1✔
135
    String parameterMap = context.getStringAttribute("parameterMap");
1✔
136
    String resultType = context.getStringAttribute("resultType");
1✔
137
    Class<?> resultTypeClass = resolveClass(resultType);
1✔
138
    String resultMap = context.getStringAttribute("resultMap");
1✔
139
    if (resultTypeClass == null && resultMap == null) {
1✔
140
      resultTypeClass = MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id);
1✔
141
    }
142
    String resultSetType = context.getStringAttribute("resultSetType");
1✔
143
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
1✔
144
    if (resultSetTypeEnum == null) {
1✔
145
      resultSetTypeEnum = configuration.getDefaultResultSetType();
1✔
146
    }
147
    String keyProperty = context.getStringAttribute("keyProperty");
1✔
148
    String keyColumn = context.getStringAttribute("keyColumn");
1✔
149
    String resultSets = context.getStringAttribute("resultSets");
1✔
150
    boolean dirtySelect = context.getBooleanAttribute("affectData", Boolean.FALSE);
1✔
151

152
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
1✔
153
        parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
154
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect, paramNameResolver);
155
  }
1✔
156

157
  private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
158
    List<XNode> selectKeyNodes = context.evalNodes("selectKey");
1✔
159
    if (configuration.getDatabaseId() != null) {
1✔
160
      parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
1✔
161
    }
162
    parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
1✔
163
    removeSelectKeyNodes(selectKeyNodes);
1✔
164
  }
1✔
165

166
  private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass,
167
      LanguageDriver langDriver, String skRequiredDatabaseId) {
168
    for (XNode nodeToHandle : list) {
1✔
169
      String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
1✔
170
      String databaseId = nodeToHandle.getStringAttribute("databaseId");
1✔
171
      if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
1✔
172
        parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
1✔
173
      }
174
    }
1✔
175
  }
1✔
176

177
  private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver,
178
      String databaseId) {
179
    String resultType = nodeToHandle.getStringAttribute("resultType");
1✔
180
    Class<?> resultTypeClass = resolveClass(resultType);
1✔
181
    StatementType statementType = StatementType
1✔
182
        .valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
1✔
183
    String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
1✔
184
    String keyColumn = nodeToHandle.getStringAttribute("keyColumn");
1✔
185
    boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));
1✔
186

187
    // defaults
188
    boolean useCache = false;
1✔
189
    boolean resultOrdered = false;
1✔
190
    KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
1✔
191
    Integer fetchSize = null;
1✔
192
    Integer timeout = null;
1✔
193
    boolean flushCache = false;
1✔
194
    String parameterMap = null;
1✔
195
    String resultMap = null;
1✔
196
    ResultSetType resultSetTypeEnum = null;
1✔
197

198
    SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
1✔
199
    SqlCommandType sqlCommandType = SqlCommandType.SELECT;
1✔
200

201
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
1✔
202
        parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
203
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null, false, null);
204

205
    id = builderAssistant.applyCurrentNamespace(id, false);
1✔
206

207
    MappedStatement keyStatement = configuration.getMappedStatement(id, false);
1✔
208
    configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
1✔
209
  }
1✔
210

211
  private void removeSelectKeyNodes(List<XNode> selectKeyNodes) {
212
    for (XNode nodeToHandle : selectKeyNodes) {
1✔
213
      nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
1✔
214
    }
1✔
215
  }
1✔
216

217
  private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
218
    if (requiredDatabaseId != null) {
1✔
219
      return requiredDatabaseId.equals(databaseId);
1✔
220
    }
221
    if (databaseId != null) {
1✔
222
      return false;
1✔
223
    }
224
    id = builderAssistant.applyCurrentNamespace(id, false);
1✔
225
    if (!this.configuration.hasStatement(id, false)) {
1✔
226
      return true;
1✔
227
    }
228
    // skip this statement if there is a previous one with a not null databaseId
229
    MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
1✔
230
    return previous.getDatabaseId() == null;
1!
231
  }
232

233
  private LanguageDriver getLanguageDriver(String lang) {
234
    Class<? extends LanguageDriver> langClass = null;
1✔
235
    if (lang != null) {
1✔
236
      langClass = resolveClass(lang);
1✔
237
    }
238
    return configuration.getLanguageDriver(langClass);
1✔
239
  }
240

241
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc