1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.builder.annotation;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.lang.annotation.Annotation;
21 import java.lang.reflect.Array;
22 import java.lang.reflect.GenericArrayType;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.ParameterizedType;
25 import java.lang.reflect.Type;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.Optional;
35 import java.util.Properties;
36 import java.util.Set;
37
38 import org.apache.ibatis.annotations.Arg;
39 import org.apache.ibatis.annotations.CacheNamespace;
40 import org.apache.ibatis.annotations.CacheNamespaceRef;
41 import org.apache.ibatis.annotations.Case;
42 import org.apache.ibatis.annotations.Delete;
43 import org.apache.ibatis.annotations.DeleteProvider;
44 import org.apache.ibatis.annotations.Insert;
45 import org.apache.ibatis.annotations.InsertProvider;
46 import org.apache.ibatis.annotations.Lang;
47 import org.apache.ibatis.annotations.MapKey;
48 import org.apache.ibatis.annotations.Options;
49 import org.apache.ibatis.annotations.Options.FlushCachePolicy;
50 import org.apache.ibatis.annotations.Property;
51 import org.apache.ibatis.annotations.Result;
52 import org.apache.ibatis.annotations.ResultMap;
53 import org.apache.ibatis.annotations.ResultType;
54 import org.apache.ibatis.annotations.Results;
55 import org.apache.ibatis.annotations.Select;
56 import org.apache.ibatis.annotations.SelectKey;
57 import org.apache.ibatis.annotations.SelectProvider;
58 import org.apache.ibatis.annotations.TypeDiscriminator;
59 import org.apache.ibatis.annotations.Update;
60 import org.apache.ibatis.annotations.UpdateProvider;
61 import org.apache.ibatis.binding.BindingException;
62 import org.apache.ibatis.binding.MapperMethod.ParamMap;
63 import org.apache.ibatis.builder.BuilderException;
64 import org.apache.ibatis.builder.CacheRefResolver;
65 import org.apache.ibatis.builder.IncompleteElementException;
66 import org.apache.ibatis.builder.MapperBuilderAssistant;
67 import org.apache.ibatis.builder.xml.XMLMapperBuilder;
68 import org.apache.ibatis.cursor.Cursor;
69 import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
70 import org.apache.ibatis.executor.keygen.KeyGenerator;
71 import org.apache.ibatis.executor.keygen.NoKeyGenerator;
72 import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
73 import org.apache.ibatis.io.Resources;
74 import org.apache.ibatis.mapping.Discriminator;
75 import org.apache.ibatis.mapping.FetchType;
76 import org.apache.ibatis.mapping.MappedStatement;
77 import org.apache.ibatis.mapping.ResultFlag;
78 import org.apache.ibatis.mapping.ResultMapping;
79 import org.apache.ibatis.mapping.ResultSetType;
80 import org.apache.ibatis.mapping.SqlCommandType;
81 import org.apache.ibatis.mapping.SqlSource;
82 import org.apache.ibatis.mapping.StatementType;
83 import org.apache.ibatis.parsing.PropertyParser;
84 import org.apache.ibatis.reflection.TypeParameterResolver;
85 import org.apache.ibatis.scripting.LanguageDriver;
86 import org.apache.ibatis.session.Configuration;
87 import org.apache.ibatis.session.ResultHandler;
88 import org.apache.ibatis.session.RowBounds;
89 import org.apache.ibatis.type.JdbcType;
90 import org.apache.ibatis.type.TypeHandler;
91 import org.apache.ibatis.type.UnknownTypeHandler;
92
93
94
95
96
97 public class MapperAnnotationBuilder {
98
99 private static final Set<Class<? extends Annotation>> SQL_ANNOTATION_TYPES = new HashSet<>();
100 private static final Set<Class<? extends Annotation>> SQL_PROVIDER_ANNOTATION_TYPES = new HashSet<>();
101
102 private final Configuration configuration;
103 private final MapperBuilderAssistant assistant;
104 private final Class<?> type;
105
106 static {
107 SQL_ANNOTATION_TYPES.add(Select.class);
108 SQL_ANNOTATION_TYPES.add(Insert.class);
109 SQL_ANNOTATION_TYPES.add(Update.class);
110 SQL_ANNOTATION_TYPES.add(Delete.class);
111
112 SQL_PROVIDER_ANNOTATION_TYPES.add(SelectProvider.class);
113 SQL_PROVIDER_ANNOTATION_TYPES.add(InsertProvider.class);
114 SQL_PROVIDER_ANNOTATION_TYPES.add(UpdateProvider.class);
115 SQL_PROVIDER_ANNOTATION_TYPES.add(DeleteProvider.class);
116 }
117
118 public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
119 String resource = type.getName().replace('.', '/') + ".java (best guess)";
120 this.assistant = new MapperBuilderAssistant(configuration, resource);
121 this.configuration = configuration;
122 this.type = type;
123 }
124
125 public void parse() {
126 String resource = type.toString();
127 if (!configuration.isResourceLoaded(resource)) {
128 loadXmlResource();
129 configuration.addLoadedResource(resource);
130 assistant.setCurrentNamespace(type.getName());
131 parseCache();
132 parseCacheRef();
133 Method[] methods = type.getMethods();
134 for (Method method : methods) {
135 try {
136
137 if (!method.isBridge()) {
138 parseStatement(method);
139 }
140 } catch (IncompleteElementException e) {
141 configuration.addIncompleteMethod(new MethodResolver(this, method));
142 }
143 }
144 }
145 parsePendingMethods();
146 }
147
148 private void parsePendingMethods() {
149 Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
150 synchronized (incompleteMethods) {
151 Iterator<MethodResolver> iter = incompleteMethods.iterator();
152 while (iter.hasNext()) {
153 try {
154 iter.next().resolve();
155 iter.remove();
156 } catch (IncompleteElementException e) {
157
158 }
159 }
160 }
161 }
162
163 private void loadXmlResource() {
164
165
166
167 if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
168 String xmlResource = type.getName().replace('.', '/') + ".xml";
169
170 InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
171 if (inputStream == null) {
172
173 try {
174 inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
175 } catch (IOException e2) {
176
177 }
178 }
179 if (inputStream != null) {
180 XMLMapperBuilderrBuilder.html#XMLMapperBuilder">XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
181 xmlParser.parse();
182 }
183 }
184 }
185
186 private void parseCache() {
187 CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
188 if (cacheDomain != null) {
189 Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
190 Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
191 Properties props = convertToProperties(cacheDomain.properties());
192 assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
193 }
194 }
195
196 private Properties convertToProperties(Property[] properties) {
197 if (properties.length == 0) {
198 return null;
199 }
200 Properties props = new Properties();
201 for (Property property : properties) {
202 props.setProperty(property.name(),
203 PropertyParser.parse(property.value(), configuration.getVariables()));
204 }
205 return props;
206 }
207
208 private void parseCacheRef() {
209 CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
210 if (cacheDomainRef != null) {
211 Class<?> refType = cacheDomainRef.value();
212 String refName = cacheDomainRef.name();
213 if (refType == void.class && refName.isEmpty()) {
214 throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
215 }
216 if (refType != void.class && !refName.isEmpty()) {
217 throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
218 }
219 String namespace = (refType != void.class) ? refType.getName() : refName;
220 try {
221 assistant.useCacheRef(namespace);
222 } catch (IncompleteElementException e) {
223 configuration.addIncompleteCacheRef(new CacheRefResolver(assistant, namespace));
224 }
225 }
226 }
227
228 private String parseResultMap(Method method) {
229 Class<?> returnType = getReturnType(method);
230 Arg[] args = method.getAnnotationsByType(Arg.class);
231 Result[] results = method.getAnnotationsByType(Result.class);
232 TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
233 String resultMapId = generateResultMapName(method);
234 applyResultMap(resultMapId, returnType, args, results, typeDiscriminator);
235 return resultMapId;
236 }
237
238 private String generateResultMapName(Method method) {
239 Results results = method.getAnnotation(Results.class);
240 if (results != null && !results.id().isEmpty()) {
241 return type.getName() + "." + results.id();
242 }
243 StringBuilder suffix = new StringBuilder();
244 for (Class<?> c : method.getParameterTypes()) {
245 suffix.append("-");
246 suffix.append(c.getSimpleName());
247 }
248 if (suffix.length() < 1) {
249 suffix.append("-void");
250 }
251 return type.getName() + "." + method.getName() + suffix;
252 }
253
254 private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) {
255 List<ResultMapping> resultMappings = new ArrayList<>();
256 applyConstructorArgs(args, returnType, resultMappings);
257 applyResults(results, returnType, resultMappings);
258 Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
259
260 assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
261 createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
262 }
263
264 private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
265 if (discriminator != null) {
266 for (Case c : discriminator.cases()) {
267 String caseResultMapId = resultMapId + "-" + c.value();
268 List<ResultMapping> resultMappings = new ArrayList<>();
269
270 applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
271 applyResults(c.results(), resultType, resultMappings);
272
273 assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
274 }
275 }
276 }
277
278 private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
279 if (discriminator != null) {
280 String column = discriminator.column();
281 Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
282 JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
283 @SuppressWarnings("unchecked")
284 Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
285 (discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler());
286 Case[] cases = discriminator.cases();
287 Map<String, String> discriminatorMap = new HashMap<>();
288 for (Case c : cases) {
289 String value = c.value();
290 String caseResultMapId = resultMapId + "-" + value;
291 discriminatorMap.put(value, caseResultMapId);
292 }
293 return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
294 }
295 return null;
296 }
297
298 void parseStatement(Method method) {
299 Class<?> parameterTypeClass = getParameterType(method);
300 LanguageDriver languageDriver = getLanguageDriver(method);
301 SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
302 if (sqlSource != null) {
303 Options options = method.getAnnotation(Options.class);
304 final String mappedStatementId = type.getName() + "." + method.getName();
305 Integer fetchSize = null;
306 Integer timeout = null;
307 StatementType statementType = StatementType.PREPARED;
308 ResultSetType resultSetType = configuration.getDefaultResultSetType();
309 SqlCommandType sqlCommandType = getSqlCommandType(method);
310 boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
311 boolean flushCache = !isSelect;
312 boolean useCache = isSelect;
313
314 KeyGenerator keyGenerator;
315 String keyProperty = null;
316 String keyColumn = null;
317 if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
318
319 SelectKey selectKey = method.getAnnotation(SelectKey.class);
320 if (selectKey != null) {
321 keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
322 keyProperty = selectKey.keyProperty();
323 } else if (options == null) {
324 keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
325 } else {
326 keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
327 keyProperty = options.keyProperty();
328 keyColumn = options.keyColumn();
329 }
330 } else {
331 keyGenerator = NoKeyGenerator.INSTANCE;
332 }
333
334 if (options != null) {
335 if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
336 flushCache = true;
337 } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
338 flushCache = false;
339 }
340 useCache = options.useCache();
341 fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null;
342 timeout = options.timeout() > -1 ? options.timeout() : null;
343 statementType = options.statementType();
344 if (options.resultSetType() != ResultSetType.DEFAULT) {
345 resultSetType = options.resultSetType();
346 }
347 }
348
349 String resultMapId = null;
350 ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
351 if (resultMapAnnotation != null) {
352 resultMapId = String.join(",", resultMapAnnotation.value());
353 } else if (isSelect) {
354 resultMapId = parseResultMap(method);
355 }
356
357 assistant.addMappedStatement(
358 mappedStatementId,
359 sqlSource,
360 statementType,
361 sqlCommandType,
362 fetchSize,
363 timeout,
364
365 null,
366 parameterTypeClass,
367 resultMapId,
368 getReturnType(method),
369 resultSetType,
370 flushCache,
371 useCache,
372
373 false,
374 keyGenerator,
375 keyProperty,
376 keyColumn,
377
378 null,
379 languageDriver,
380
381 options != null ? nullOrEmpty(options.resultSets()) : null);
382 }
383 }
384
385 private LanguageDriver getLanguageDriver(Method method) {
386 Lang lang = method.getAnnotation(Lang.class);
387 Class<? extends LanguageDriver> langClass = null;
388 if (lang != null) {
389 langClass = lang.value();
390 }
391 return configuration.getLanguageDriver(langClass);
392 }
393
394 private Class<?> getParameterType(Method method) {
395 Class<?> parameterType = null;
396 Class<?>[] parameterTypes = method.getParameterTypes();
397 for (Class<?> currentParameterType : parameterTypes) {
398 if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
399 if (parameterType == null) {
400 parameterType = currentParameterType;
401 } else {
402
403 parameterType = ParamMap.class;
404 }
405 }
406 }
407 return parameterType;
408 }
409
410 private Class<?> getReturnType(Method method) {
411 Class<?> returnType = method.getReturnType();
412 Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
413 if (resolvedReturnType instanceof Class) {
414 returnType = (Class<?>) resolvedReturnType;
415 if (returnType.isArray()) {
416 returnType = returnType.getComponentType();
417 }
418
419 if (void.class.equals(returnType)) {
420 ResultType rt = method.getAnnotation(ResultType.class);
421 if (rt != null) {
422 returnType = rt.value();
423 }
424 }
425 } else if (resolvedReturnType instanceof ParameterizedType) {
426 ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
427 Class<?> rawType = (Class<?>) parameterizedType.getRawType();
428 if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
429 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
430 if (actualTypeArguments != null && actualTypeArguments.length == 1) {
431 Type returnTypeParameter = actualTypeArguments[0];
432 if (returnTypeParameter instanceof Class<?>) {
433 returnType = (Class<?>) returnTypeParameter;
434 } else if (returnTypeParameter instanceof ParameterizedType) {
435
436 returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
437 } else if (returnTypeParameter instanceof GenericArrayType) {
438 Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
439
440 returnType = Array.newInstance(componentType, 0).getClass();
441 }
442 }
443 } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
444
445 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
446 if (actualTypeArguments != null && actualTypeArguments.length == 2) {
447 Type returnTypeParameter = actualTypeArguments[1];
448 if (returnTypeParameter instanceof Class<?>) {
449 returnType = (Class<?>) returnTypeParameter;
450 } else if (returnTypeParameter instanceof ParameterizedType) {
451
452 returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
453 }
454 }
455 } else if (Optional.class.equals(rawType)) {
456 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
457 Type returnTypeParameter = actualTypeArguments[0];
458 if (returnTypeParameter instanceof Class<?>) {
459 returnType = (Class<?>) returnTypeParameter;
460 }
461 }
462 }
463
464 return returnType;
465 }
466
467 private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
468 try {
469 Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
470 Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
471 if (sqlAnnotationType != null) {
472 if (sqlProviderAnnotationType != null) {
473 throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
474 }
475 Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
476 final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
477 return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
478 } else if (sqlProviderAnnotationType != null) {
479 Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
480 return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
481 }
482 return null;
483 } catch (Exception e) {
484 throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
485 }
486 }
487
488 private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
489 final StringBuilder sql = new StringBuilder();
490 for (String fragment : strings) {
491 sql.append(fragment);
492 sql.append(" ");
493 }
494 return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
495 }
496
497 private SqlCommandType getSqlCommandType(Method method) {
498 Class<? extends Annotation> type = getSqlAnnotationType(method);
499
500 if (type == null) {
501 type = getSqlProviderAnnotationType(method);
502
503 if (type == null) {
504 return SqlCommandType.UNKNOWN;
505 }
506
507 if (type == SelectProvider.class) {
508 type = Select.class;
509 } else if (type == InsertProvider.class) {
510 type = Insert.class;
511 } else if (type == UpdateProvider.class) {
512 type = Update.class;
513 } else if (type == DeleteProvider.class) {
514 type = Delete.class;
515 }
516 }
517
518 return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
519 }
520
521 private Class<? extends Annotation> getSqlAnnotationType(Method method) {
522 return chooseAnnotationType(method, SQL_ANNOTATION_TYPES);
523 }
524
525 private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
526 return chooseAnnotationType(method, SQL_PROVIDER_ANNOTATION_TYPES);
527 }
528
529 private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
530 for (Class<? extends Annotation> type : types) {
531 Annotation annotation = method.getAnnotation(type);
532 if (annotation != null) {
533 return type;
534 }
535 }
536 return null;
537 }
538
539 private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
540 for (Result result : results) {
541 List<ResultFlag> flags = new ArrayList<>();
542 if (result.id()) {
543 flags.add(ResultFlag.ID);
544 }
545 @SuppressWarnings("unchecked")
546 Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
547 ((result.typeHandler() == UnknownTypeHandler.class) ? null : result.typeHandler());
548 ResultMapping resultMapping = assistant.buildResultMapping(
549 resultType,
550 nullOrEmpty(result.property()),
551 nullOrEmpty(result.column()),
552 result.javaType() == void.class ? null : result.javaType(),
553 result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
554 hasNestedSelect(result) ? nestedSelectId(result) : null,
555 null,
556 null,
557 null,
558 typeHandler,
559 flags,
560 null,
561 null,
562 isLazy(result));
563 resultMappings.add(resultMapping);
564 }
565 }
566
567 private String nestedSelectId(Result result) {
568 String nestedSelect = result.one().select();
569 if (nestedSelect.length() < 1) {
570 nestedSelect = result.many().select();
571 }
572 if (!nestedSelect.contains(".")) {
573 nestedSelect = type.getName() + "." + nestedSelect;
574 }
575 return nestedSelect;
576 }
577
578 private boolean isLazy(Result result) {
579 boolean isLazy = configuration.isLazyLoadingEnabled();
580 if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
581 isLazy = result.one().fetchType() == FetchType.LAZY;
582 } else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
583 isLazy = result.many().fetchType() == FetchType.LAZY;
584 }
585 return isLazy;
586 }
587
588 private boolean hasNestedSelect(Result result) {
589 if (result.one().select().length() > 0 && result.many().select().length() > 0) {
590 throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
591 }
592 return result.one().select().length() > 0 || result.many().select().length() > 0;
593 }
594
595 private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
596 for (Arg arg : args) {
597 List<ResultFlag> flags = new ArrayList<>();
598 flags.add(ResultFlag.CONSTRUCTOR);
599 if (arg.id()) {
600 flags.add(ResultFlag.ID);
601 }
602 @SuppressWarnings("unchecked")
603 Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
604 (arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler());
605 ResultMapping resultMapping = assistant.buildResultMapping(
606 resultType,
607 nullOrEmpty(arg.name()),
608 nullOrEmpty(arg.column()),
609 arg.javaType() == void.class ? null : arg.javaType(),
610 arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
611 nullOrEmpty(arg.select()),
612 nullOrEmpty(arg.resultMap()),
613 null,
614 nullOrEmpty(arg.columnPrefix()),
615 typeHandler,
616 flags,
617 null,
618 null,
619 false);
620 resultMappings.add(resultMapping);
621 }
622 }
623
624 private String nullOrEmpty(String value) {
625 return value == null || value.trim().length() == 0 ? null : value;
626 }
627
628 private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
629 String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
630 Class<?> resultTypeClass = selectKeyAnnotation.resultType();
631 StatementType statementType = selectKeyAnnotation.statementType();
632 String keyProperty = selectKeyAnnotation.keyProperty();
633 String keyColumn = selectKeyAnnotation.keyColumn();
634 boolean executeBefore = selectKeyAnnotation.before();
635
636
637 boolean useCache = false;
638 KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
639 Integer fetchSize = null;
640 Integer timeout = null;
641 boolean flushCache = false;
642 String parameterMap = null;
643 String resultMap = null;
644 ResultSetType resultSetTypeEnum = null;
645
646 SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
647 SqlCommandType sqlCommandType = SqlCommandType.SELECT;
648
649 assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum,
650 flushCache, useCache, false,
651 keyGenerator, keyProperty, keyColumn, null, languageDriver, null);
652
653 id = assistant.applyCurrentNamespace(id, false);
654
655 MappedStatement keyStatement = configuration.getMappedStatement(id, false);
656 SelectKeyGeneratorectKeyGenerator.html#SelectKeyGenerator">SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
657 configuration.addKeyGenerator(id, answer);
658 return answer;
659 }
660
661 }