MybatisMapperAnnotationBuilder.java 30 KB

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