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