|
@@ -1,38 +1,21 @@
|
|
|
/**
|
|
|
- * Copyright 2009-2016 the original author or authors.
|
|
|
- *
|
|
|
- * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
- * you may not use this file except in compliance with the License.
|
|
|
- * You may obtain a copy of the License at
|
|
|
- *
|
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
- *
|
|
|
- * Unless required by applicable law or agreed to in writing, software
|
|
|
- * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
- * See the License for the specific language governing permissions and
|
|
|
- * limitations under the License.
|
|
|
+ * Copyright 2009-2016 the original author or authors.
|
|
|
+ * <p>
|
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+ * you may not use this file except in compliance with the License.
|
|
|
+ * You may obtain a copy of the License at
|
|
|
+ * <p>
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+ * <p>
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+ * See the License for the specific language governing permissions and
|
|
|
+ * limitations under the License.
|
|
|
*/
|
|
|
package com.baomidou.mybatisplus;
|
|
|
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.InputStream;
|
|
|
-import java.lang.annotation.Annotation;
|
|
|
-import java.lang.reflect.Array;
|
|
|
-import java.lang.reflect.GenericArrayType;
|
|
|
-import java.lang.reflect.Method;
|
|
|
-import java.lang.reflect.ParameterizedType;
|
|
|
-import java.lang.reflect.Type;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Collection;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.HashSet;
|
|
|
-import java.util.Iterator;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Locale;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.Set;
|
|
|
-
|
|
|
+import com.baomidou.mybatisplus.mapper.BaseMapper;
|
|
|
import org.apache.ibatis.annotations.Arg;
|
|
|
import org.apache.ibatis.annotations.CacheNamespace;
|
|
|
import org.apache.ibatis.annotations.CacheNamespaceRef;
|
|
@@ -45,6 +28,7 @@ import org.apache.ibatis.annotations.InsertProvider;
|
|
|
import org.apache.ibatis.annotations.Lang;
|
|
|
import org.apache.ibatis.annotations.MapKey;
|
|
|
import org.apache.ibatis.annotations.Options;
|
|
|
+import org.apache.ibatis.annotations.Options.FlushCachePolicy;
|
|
|
import org.apache.ibatis.annotations.Result;
|
|
|
import org.apache.ibatis.annotations.ResultMap;
|
|
|
import org.apache.ibatis.annotations.ResultType;
|
|
@@ -55,7 +39,6 @@ import org.apache.ibatis.annotations.SelectProvider;
|
|
|
import org.apache.ibatis.annotations.TypeDiscriminator;
|
|
|
import org.apache.ibatis.annotations.Update;
|
|
|
import org.apache.ibatis.annotations.UpdateProvider;
|
|
|
-import org.apache.ibatis.annotations.Options.FlushCachePolicy;
|
|
|
import org.apache.ibatis.binding.BindingException;
|
|
|
import org.apache.ibatis.binding.MapperMethod.ParamMap;
|
|
|
import org.apache.ibatis.builder.BuilderException;
|
|
@@ -64,7 +47,6 @@ import org.apache.ibatis.builder.MapperBuilderAssistant;
|
|
|
import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
|
|
|
import org.apache.ibatis.builder.annotation.MethodResolver;
|
|
|
import org.apache.ibatis.builder.annotation.ProviderSqlSource;
|
|
|
-import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
|
|
import org.apache.ibatis.cursor.Cursor;
|
|
|
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
|
|
|
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
|
@@ -89,538 +71,545 @@ import org.apache.ibatis.type.JdbcType;
|
|
|
import org.apache.ibatis.type.TypeHandler;
|
|
|
import org.apache.ibatis.type.UnknownTypeHandler;
|
|
|
|
|
|
-import com.baomidou.mybatisplus.mapper.BaseMapper;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.lang.annotation.Annotation;
|
|
|
+import java.lang.reflect.Array;
|
|
|
+import java.lang.reflect.GenericArrayType;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.lang.reflect.ParameterizedType;
|
|
|
+import java.lang.reflect.Type;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Locale;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
|
|
|
/**
|
|
|
* 继承 MapperAnnotationBuilder 没有XML配置文件注入基础CRUD方法
|
|
|
- *
|
|
|
+ *
|
|
|
* @author Caratacus
|
|
|
*/
|
|
|
public class MybatisPlusMapperBuilder extends MapperAnnotationBuilder {
|
|
|
|
|
|
- private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
|
|
|
- private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();
|
|
|
-
|
|
|
- private Configuration configuration;
|
|
|
- private MapperBuilderAssistant assistant;
|
|
|
- private Class<?> type;
|
|
|
-
|
|
|
- public MybatisPlusMapperBuilder(Configuration configuration, Class<?> type) {
|
|
|
- super(configuration, type);
|
|
|
- String resource = type.getName().replace('.', '/') + ".java (best guess)";
|
|
|
- this.assistant = new MapperBuilderAssistant(configuration, resource);
|
|
|
- this.configuration = configuration;
|
|
|
- this.type = type;
|
|
|
-
|
|
|
- sqlAnnotationTypes.add(Select.class);
|
|
|
- sqlAnnotationTypes.add(Insert.class);
|
|
|
- sqlAnnotationTypes.add(Update.class);
|
|
|
- sqlAnnotationTypes.add(Delete.class);
|
|
|
-
|
|
|
- sqlProviderAnnotationTypes.add(SelectProvider.class);
|
|
|
- sqlProviderAnnotationTypes.add(InsertProvider.class);
|
|
|
- sqlProviderAnnotationTypes.add(UpdateProvider.class);
|
|
|
- sqlProviderAnnotationTypes.add(DeleteProvider.class);
|
|
|
- }
|
|
|
-
|
|
|
- public void parse() {
|
|
|
- String resource = type.toString();
|
|
|
-
|
|
|
- if (!configuration.isResourceLoaded(resource)) {
|
|
|
- loadXmlResource();
|
|
|
- configuration.addLoadedResource(resource);
|
|
|
- assistant.setCurrentNamespace(type.getName());
|
|
|
- parseCache();
|
|
|
- parseCacheRef();
|
|
|
- Method[] methods = type.getMethods();
|
|
|
- inspectInject();
|
|
|
- for (Method method : methods) {
|
|
|
- try {
|
|
|
- // issue #237
|
|
|
- if (!method.isBridge()) {
|
|
|
- parseStatement(method);
|
|
|
- }
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- configuration.addIncompleteMethod(new MethodResolver(this, method));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- parsePendingMethods();
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * 注入 CURD 动态 SQL
|
|
|
- */
|
|
|
- private void inspectInject() {
|
|
|
- if (BaseMapper.class.isAssignableFrom(type)) {
|
|
|
- MybatisConfiguration.SQL_INJECTOR.inject(configuration, assistant, type);
|
|
|
+ private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
|
|
|
+ private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();
|
|
|
+
|
|
|
+ private Configuration configuration;
|
|
|
+ private MapperBuilderAssistant assistant;
|
|
|
+ private Class<?> type;
|
|
|
+
|
|
|
+ public MybatisPlusMapperBuilder(Configuration configuration, Class<?> type) {
|
|
|
+ super(configuration, type);
|
|
|
+ String resource = type.getName().replace('.', '/') + ".java (best guess)";
|
|
|
+ this.assistant = new MapperBuilderAssistant(configuration, resource);
|
|
|
+ this.configuration = configuration;
|
|
|
+ this.type = type;
|
|
|
+ sqlAnnotationTypes.add(Select.class);
|
|
|
+ sqlAnnotationTypes.add(Insert.class);
|
|
|
+ sqlAnnotationTypes.add(Update.class);
|
|
|
+ sqlAnnotationTypes.add(Delete.class);
|
|
|
+ sqlProviderAnnotationTypes.add(SelectProvider.class);
|
|
|
+ sqlProviderAnnotationTypes.add(InsertProvider.class);
|
|
|
+ sqlProviderAnnotationTypes.add(UpdateProvider.class);
|
|
|
+ sqlProviderAnnotationTypes.add(DeleteProvider.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void parse() {
|
|
|
+ String resource = type.toString();
|
|
|
+ if (!configuration.isResourceLoaded(resource)) {
|
|
|
+ boolean existXml = loadXmlResource();
|
|
|
+ configuration.addLoadedResource(resource);
|
|
|
+ assistant.setCurrentNamespace(type.getName());
|
|
|
+ parseCache();
|
|
|
+ parseCacheRef();
|
|
|
+ Method[] methods = type.getMethods();
|
|
|
+ for (Method method : methods) {
|
|
|
+ try {
|
|
|
+ // issue #237
|
|
|
+ if (!method.isBridge()) {
|
|
|
+ parseStatement(method);
|
|
|
+ }
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ configuration.addIncompleteMethod(new MethodResolver(this, method));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ inspectInject(existXml);
|
|
|
+ }
|
|
|
+ parsePendingMethods();
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 注入 CURD 动态 SQL(XML不存在时注入)
|
|
|
+ */
|
|
|
+ private void inspectInject(boolean flag) {
|
|
|
+ if (!flag && BaseMapper.class.isAssignableFrom(type)) {
|
|
|
+ MybatisConfiguration.SQL_INJECTOR.inspectInject(configuration, assistant, type);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parsePendingMethods() {
|
|
|
+ Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
|
|
|
+ synchronized (incompleteMethods) {
|
|
|
+ Iterator<MethodResolver> iter = incompleteMethods.iterator();
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ try {
|
|
|
+ iter.next().resolve();
|
|
|
+ iter.remove();
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ // This method is still missing a resource
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是否存在XML
|
|
|
+ *
|
|
|
+ * @return boolean ture存在,flase不存在
|
|
|
+ */
|
|
|
+ private boolean loadXmlResource() {
|
|
|
+ boolean flag = true;
|
|
|
+ // Spring may not know the real resource name so we check a flag
|
|
|
+ // to prevent loading again a resource twice
|
|
|
+ // this flag is set at MybatisXMLMapperBuilder#bindMapperForNamespace
|
|
|
+ if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
|
|
|
+ String xmlResource = type.getName().replace('.', '/') + ".xml";
|
|
|
+ InputStream inputStream = null;
|
|
|
+ try {
|
|
|
+ inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
|
|
|
+ } catch (IOException e) {
|
|
|
+ // ignore, resource is not required
|
|
|
+ flag = false;
|
|
|
+ }
|
|
|
+ if (inputStream != null) {
|
|
|
+ MybatisXMLMapperBuilder xmlParser = new MybatisXMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource,
|
|
|
+ configuration.getSqlFragments(), type.getName());
|
|
|
+ xmlParser.parse();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return flag;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parseCache() {
|
|
|
+ CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
|
|
|
+ if (cacheDomain != null) {
|
|
|
+ Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
|
|
|
+ Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
|
|
|
+ assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size,
|
|
|
+ cacheDomain.readWrite(), cacheDomain.blocking(), null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parseCacheRef() {
|
|
|
+ CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
|
|
|
+ if (cacheDomainRef != null) {
|
|
|
+ assistant.useCacheRef(cacheDomainRef.value().getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String parseResultMap(Method method) {
|
|
|
+ Class<?> returnType = getReturnType(method);
|
|
|
+ ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
|
|
|
+ Results results = method.getAnnotation(Results.class);
|
|
|
+ TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
|
|
|
+ String resultMapId = generateResultMapName(method);
|
|
|
+ applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
|
|
|
+ return resultMapId;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateResultMapName(Method method) {
|
|
|
+ Results results = method.getAnnotation(Results.class);
|
|
|
+ if (results != null && !results.id().isEmpty()) {
|
|
|
+ return type.getName() + "." + results.id();
|
|
|
+ }
|
|
|
+ StringBuilder suffix = new StringBuilder();
|
|
|
+ for (Class<?> c : method.getParameterTypes()) {
|
|
|
+ suffix.append("-");
|
|
|
+ suffix.append(c.getSimpleName());
|
|
|
+ }
|
|
|
+ if (suffix.length() < 1) {
|
|
|
+ suffix.append("-void");
|
|
|
+ }
|
|
|
+ return type.getName() + "." + method.getName() + suffix;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results,
|
|
|
+ TypeDiscriminator discriminator) {
|
|
|
+ List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
|
|
|
+ applyConstructorArgs(args, returnType, resultMappings);
|
|
|
+ applyResults(results, returnType, resultMappings);
|
|
|
+ Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
|
|
|
+ // TODO add AutoMappingBehaviour
|
|
|
+ assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
|
|
|
+ createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
|
|
|
+ if (discriminator != null) {
|
|
|
+ for (Case c : discriminator.cases()) {
|
|
|
+ String caseResultMapId = resultMapId + "-" + c.value();
|
|
|
+ List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
|
|
|
+ // issue #136
|
|
|
+ applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
|
|
|
+ applyResults(c.results(), resultType, resultMappings);
|
|
|
+ // TODO add AutoMappingBehaviour
|
|
|
+ assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
|
|
|
+ if (discriminator != null) {
|
|
|
+ String column = discriminator.column();
|
|
|
+ Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
|
|
|
+ JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (discriminator.typeHandler() == UnknownTypeHandler.class ? null
|
|
|
+ : discriminator.typeHandler());
|
|
|
+ Case[] cases = discriminator.cases();
|
|
|
+ Map<String, String> discriminatorMap = new HashMap<String, String>();
|
|
|
+ for (Case c : cases) {
|
|
|
+ String value = c.value();
|
|
|
+ String caseResultMapId = resultMapId + "-" + value;
|
|
|
+ discriminatorMap.put(value, caseResultMapId);
|
|
|
+ }
|
|
|
+ return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ void parseStatement(Method method) {
|
|
|
+ Class<?> parameterTypeClass = getParameterType(method);
|
|
|
+ LanguageDriver languageDriver = getLanguageDriver(method);
|
|
|
+ SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
|
|
|
+ if (sqlSource != null) {
|
|
|
+ Options options = method.getAnnotation(Options.class);
|
|
|
+ final String mappedStatementId = type.getName() + "." + method.getName();
|
|
|
+ Integer fetchSize = null;
|
|
|
+ Integer timeout = null;
|
|
|
+ StatementType statementType = StatementType.PREPARED;
|
|
|
+ ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
|
|
|
+ SqlCommandType sqlCommandType = getSqlCommandType(method);
|
|
|
+ boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
|
|
|
+ boolean flushCache = !isSelect;
|
|
|
+ boolean useCache = isSelect;
|
|
|
+ KeyGenerator keyGenerator;
|
|
|
+ String keyProperty = "id";
|
|
|
+ String keyColumn = null;
|
|
|
+ if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
|
|
|
+ // first check for SelectKey annotation - that overrides
|
|
|
+ // everything else
|
|
|
+ SelectKey selectKey = method.getAnnotation(SelectKey.class);
|
|
|
+ if (selectKey != null) {
|
|
|
+ keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method),
|
|
|
+ languageDriver);
|
|
|
+ keyProperty = selectKey.keyProperty();
|
|
|
+ } else if (options == null) {
|
|
|
+ keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
|
|
|
+ } else {
|
|
|
+ keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
|
|
|
+ keyProperty = options.keyProperty();
|
|
|
+ keyColumn = options.keyColumn();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ keyGenerator = new NoKeyGenerator();
|
|
|
+ }
|
|
|
+ if (options != null) {
|
|
|
+ if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
|
|
|
+ flushCache = true;
|
|
|
+ } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
|
|
|
+ flushCache = false;
|
|
|
+ }
|
|
|
+ useCache = options.useCache();
|
|
|
+ fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; // issue
|
|
|
+ // #348
|
|
|
+ timeout = options.timeout() > -1 ? options.timeout() : null;
|
|
|
+ statementType = options.statementType();
|
|
|
+ resultSetType = options.resultSetType();
|
|
|
+ }
|
|
|
+ String resultMapId = null;
|
|
|
+ ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
|
|
|
+ if (resultMapAnnotation != null) {
|
|
|
+ String[] resultMaps = resultMapAnnotation.value();
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (String resultMap : resultMaps) {
|
|
|
+ if (sb.length() > 0) {
|
|
|
+ sb.append(",");
|
|
|
+ }
|
|
|
+ sb.append(resultMap);
|
|
|
+ }
|
|
|
+ resultMapId = sb.toString();
|
|
|
+ } else if (isSelect) {
|
|
|
+ resultMapId = parseResultMap(method);
|
|
|
+ }
|
|
|
+ assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
|
|
|
+ // ParameterMapID
|
|
|
+ null, parameterTypeClass, resultMapId, getReturnType(method), resultSetType, flushCache, useCache,
|
|
|
+ // TODO gcode issue #577
|
|
|
+ false, keyGenerator, keyProperty, keyColumn,
|
|
|
+ // DatabaseID
|
|
|
+ null, languageDriver,
|
|
|
+ // ResultSets
|
|
|
+ options != null ? nullOrEmpty(options.resultSets()) : null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private LanguageDriver getLanguageDriver(Method method) {
|
|
|
+ Lang lang = method.getAnnotation(Lang.class);
|
|
|
+ Class<?> langClass = null;
|
|
|
+ if (lang != null) {
|
|
|
+ langClass = lang.value();
|
|
|
+ }
|
|
|
+ return assistant.getLanguageDriver(langClass);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Class<?> getParameterType(Method method) {
|
|
|
+ Class<?> parameterType = null;
|
|
|
+ Class<?>[] parameterTypes = method.getParameterTypes();
|
|
|
+ for (Class<?> currentParameterType : parameterTypes) {
|
|
|
+ if (!RowBounds.class.isAssignableFrom(currentParameterType)
|
|
|
+ && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
|
|
|
+ if (parameterType == null) {
|
|
|
+ parameterType = currentParameterType;
|
|
|
+ } else {
|
|
|
+ // issue #135
|
|
|
+ parameterType = ParamMap.class;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return parameterType;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Class<?> getReturnType(Method method) {
|
|
|
+ Class<?> returnType = method.getReturnType();
|
|
|
+ Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
|
|
|
+ if (resolvedReturnType instanceof Class) {
|
|
|
+ returnType = (Class<?>) resolvedReturnType;
|
|
|
+ if (returnType.isArray()) {
|
|
|
+ returnType = returnType.getComponentType();
|
|
|
+ }
|
|
|
+ // gcode issue #508
|
|
|
+ if (void.class.equals(returnType)) {
|
|
|
+ ResultType rt = method.getAnnotation(ResultType.class);
|
|
|
+ if (rt != null) {
|
|
|
+ returnType = rt.value();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (resolvedReturnType instanceof ParameterizedType) {
|
|
|
+ ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
|
|
|
+ Class<?> rawType = (Class<?>) parameterizedType.getRawType();
|
|
|
+ if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
|
|
|
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
|
|
+ if (actualTypeArguments != null && actualTypeArguments.length == 1) {
|
|
|
+ Type returnTypeParameter = actualTypeArguments[0];
|
|
|
+ if (returnTypeParameter instanceof Class<?>) {
|
|
|
+ returnType = (Class<?>) returnTypeParameter;
|
|
|
+ } else if (returnTypeParameter instanceof ParameterizedType) {
|
|
|
+ // (gcode issue #443) actual type can be a also a
|
|
|
+ // parameterized type
|
|
|
+ returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
|
|
|
+ } else if (returnTypeParameter instanceof GenericArrayType) {
|
|
|
+ Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
|
|
|
+ // (gcode issue #525) support List<byte[]>
|
|
|
+ returnType = Array.newInstance(componentType, 0).getClass();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
|
|
|
+ // (gcode issue 504) Do not look into Maps if there is not
|
|
|
+ // MapKey annotation
|
|
|
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
|
|
+ if (actualTypeArguments != null && actualTypeArguments.length == 2) {
|
|
|
+ Type returnTypeParameter = actualTypeArguments[1];
|
|
|
+ if (returnTypeParameter instanceof Class<?>) {
|
|
|
+ returnType = (Class<?>) returnTypeParameter;
|
|
|
+ } else if (returnTypeParameter instanceof ParameterizedType) {
|
|
|
+ // (gcode issue 443) actual type can be a also a
|
|
|
+ // parameterized type
|
|
|
+ returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return returnType;
|
|
|
+ }
|
|
|
+
|
|
|
+ private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
|
|
|
+ try {
|
|
|
+ Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
|
|
|
+ Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
|
|
|
+ if (sqlAnnotationType != null) {
|
|
|
+ if (sqlProviderAnnotationType != null) {
|
|
|
+ throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named "
|
|
|
+ + method.getName());
|
|
|
+ }
|
|
|
+ Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
|
|
|
+ final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
|
|
|
+ return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
|
|
|
+ } else if (sqlProviderAnnotationType != null) {
|
|
|
+ Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
|
|
|
+ return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
|
|
|
+ final StringBuilder sql = new StringBuilder();
|
|
|
+ for (String fragment : strings) {
|
|
|
+ sql.append(fragment);
|
|
|
+ sql.append(" ");
|
|
|
+ }
|
|
|
+ return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
|
|
|
+ }
|
|
|
+
|
|
|
+ private SqlCommandType getSqlCommandType(Method method) {
|
|
|
+ Class<? extends Annotation> type = getSqlAnnotationType(method);
|
|
|
+ if (type == null) {
|
|
|
+ type = getSqlProviderAnnotationType(method);
|
|
|
+ if (type == null) {
|
|
|
+ return SqlCommandType.UNKNOWN;
|
|
|
+ }
|
|
|
+ if (type == SelectProvider.class) {
|
|
|
+ type = Select.class;
|
|
|
+ } else if (type == InsertProvider.class) {
|
|
|
+ type = Insert.class;
|
|
|
+ } else if (type == UpdateProvider.class) {
|
|
|
+ type = Update.class;
|
|
|
+ } else if (type == DeleteProvider.class) {
|
|
|
+ type = Delete.class;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Class<? extends Annotation> getSqlAnnotationType(Method method) {
|
|
|
+ return chooseAnnotationType(method, sqlAnnotationTypes);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
|
|
|
+ return chooseAnnotationType(method, sqlProviderAnnotationTypes);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
|
|
|
+ for (Class<? extends Annotation> type : types) {
|
|
|
+ Annotation annotation = method.getAnnotation(type);
|
|
|
+ if (annotation != null) {
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
|
|
|
+ for (Result result : results) {
|
|
|
+ List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
+ if (result.id()) {
|
|
|
+ flags.add(ResultFlag.ID);
|
|
|
+ }
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) ((result.typeHandler() == UnknownTypeHandler.class) ? null
|
|
|
+ : result.typeHandler());
|
|
|
+ ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(result.property()),
|
|
|
+ nullOrEmpty(result.column()), result.javaType() == void.class ? null : result.javaType(),
|
|
|
+ result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
|
|
|
+ hasNestedSelect(result) ? nestedSelectId(result) : null, null, null, null, typeHandler, flags, null, null,
|
|
|
+ isLazy(result));
|
|
|
+ resultMappings.add(resultMapping);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String nestedSelectId(Result result) {
|
|
|
+ String nestedSelect = result.one().select();
|
|
|
+ if (nestedSelect.length() < 1) {
|
|
|
+ nestedSelect = result.many().select();
|
|
|
+ }
|
|
|
+ if (!nestedSelect.contains(".")) {
|
|
|
+ nestedSelect = type.getName() + "." + nestedSelect;
|
|
|
+ }
|
|
|
+ return nestedSelect;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isLazy(Result result) {
|
|
|
+ boolean isLazy = configuration.isLazyLoadingEnabled();
|
|
|
+ if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
|
|
|
+ isLazy = (result.one().fetchType() == FetchType.LAZY);
|
|
|
+ } else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
|
|
|
+ isLazy = (result.many().fetchType() == FetchType.LAZY);
|
|
|
+ }
|
|
|
+ return isLazy;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean hasNestedSelect(Result result) {
|
|
|
+ if (result.one().select().length() > 0 && result.many().select().length() > 0) {
|
|
|
+ throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
|
|
|
+ }
|
|
|
+ return result.one().select().length() > 0 || result.many().select().length() > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
|
|
|
+ for (Arg arg : args) {
|
|
|
+ List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
+ flags.add(ResultFlag.CONSTRUCTOR);
|
|
|
+ if (arg.id()) {
|
|
|
+ flags.add(ResultFlag.ID);
|
|
|
+ }
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (arg.typeHandler() == UnknownTypeHandler.class ? null
|
|
|
+ : arg.typeHandler());
|
|
|
+ ResultMapping resultMapping = assistant.buildResultMapping(resultType, null, nullOrEmpty(arg.column()),
|
|
|
+ arg.javaType() == void.class ? null : arg.javaType(),
|
|
|
+ arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(), nullOrEmpty(arg.select()),
|
|
|
+ nullOrEmpty(arg.resultMap()), null, null, typeHandler, flags, null, null, false);
|
|
|
+ resultMappings.add(resultMapping);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- private void parsePendingMethods() {
|
|
|
- Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
|
|
|
- synchronized (incompleteMethods) {
|
|
|
- Iterator<MethodResolver> iter = incompleteMethods.iterator();
|
|
|
- while (iter.hasNext()) {
|
|
|
- try {
|
|
|
- iter.next().resolve();
|
|
|
- iter.remove();
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- // This method is still missing a resource
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void loadXmlResource() {
|
|
|
- // Spring may not know the real resource name so we check a flag
|
|
|
- // to prevent loading again a resource twice
|
|
|
- // this flag is set at XMLMapperBuilder#bindMapperForNamespace
|
|
|
- if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
|
|
|
- String xmlResource = type.getName().replace('.', '/') + ".xml";
|
|
|
- InputStream inputStream = null;
|
|
|
- try {
|
|
|
- inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
|
|
|
- } catch (IOException e) {
|
|
|
- // ignore, resource is not required
|
|
|
- }
|
|
|
- if (inputStream != null) {
|
|
|
- XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource,
|
|
|
- configuration.getSqlFragments(), type.getName());
|
|
|
- xmlParser.parse();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parseCache() {
|
|
|
- CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
|
|
|
- if (cacheDomain != null) {
|
|
|
- Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
|
|
|
- Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
|
|
|
- assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size,
|
|
|
- cacheDomain.readWrite(), cacheDomain.blocking(), null);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parseCacheRef() {
|
|
|
- CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
|
|
|
- if (cacheDomainRef != null) {
|
|
|
- assistant.useCacheRef(cacheDomainRef.value().getName());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private String parseResultMap(Method method) {
|
|
|
- Class<?> returnType = getReturnType(method);
|
|
|
- ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
|
|
|
- Results results = method.getAnnotation(Results.class);
|
|
|
- TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
|
|
|
- String resultMapId = generateResultMapName(method);
|
|
|
- applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
|
|
|
- return resultMapId;
|
|
|
- }
|
|
|
-
|
|
|
- private String generateResultMapName(Method method) {
|
|
|
- Results results = method.getAnnotation(Results.class);
|
|
|
- if (results != null && !results.id().isEmpty()) {
|
|
|
- return type.getName() + "." + results.id();
|
|
|
- }
|
|
|
- StringBuilder suffix = new StringBuilder();
|
|
|
- for (Class<?> c : method.getParameterTypes()) {
|
|
|
- suffix.append("-");
|
|
|
- suffix.append(c.getSimpleName());
|
|
|
- }
|
|
|
- if (suffix.length() < 1) {
|
|
|
- suffix.append("-void");
|
|
|
- }
|
|
|
- return type.getName() + "." + method.getName() + suffix;
|
|
|
- }
|
|
|
-
|
|
|
- private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results,
|
|
|
- TypeDiscriminator discriminator) {
|
|
|
- List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
|
|
|
- applyConstructorArgs(args, returnType, resultMappings);
|
|
|
- applyResults(results, returnType, resultMappings);
|
|
|
- Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
|
|
|
- // TODO add AutoMappingBehaviour
|
|
|
- assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
|
|
|
- createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
|
|
|
- }
|
|
|
-
|
|
|
- private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
|
|
|
- if (discriminator != null) {
|
|
|
- for (Case c : discriminator.cases()) {
|
|
|
- String caseResultMapId = resultMapId + "-" + c.value();
|
|
|
- List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
|
|
|
- // issue #136
|
|
|
- applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
|
|
|
- applyResults(c.results(), resultType, resultMappings);
|
|
|
- // TODO add AutoMappingBehaviour
|
|
|
- assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
|
|
|
- if (discriminator != null) {
|
|
|
- String column = discriminator.column();
|
|
|
- Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
|
|
|
- JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (discriminator.typeHandler() == UnknownTypeHandler.class ? null
|
|
|
- : discriminator.typeHandler());
|
|
|
- Case[] cases = discriminator.cases();
|
|
|
- Map<String, String> discriminatorMap = new HashMap<String, String>();
|
|
|
- for (Case c : cases) {
|
|
|
- String value = c.value();
|
|
|
- String caseResultMapId = resultMapId + "-" + value;
|
|
|
- discriminatorMap.put(value, caseResultMapId);
|
|
|
- }
|
|
|
- return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- void parseStatement(Method method) {
|
|
|
- Class<?> parameterTypeClass = getParameterType(method);
|
|
|
- LanguageDriver languageDriver = getLanguageDriver(method);
|
|
|
- SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
|
|
|
- if (sqlSource != null) {
|
|
|
- Options options = method.getAnnotation(Options.class);
|
|
|
- final String mappedStatementId = type.getName() + "." + method.getName();
|
|
|
- Integer fetchSize = null;
|
|
|
- Integer timeout = null;
|
|
|
- StatementType statementType = StatementType.PREPARED;
|
|
|
- ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
|
|
|
- SqlCommandType sqlCommandType = getSqlCommandType(method);
|
|
|
- boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
|
|
|
- boolean flushCache = !isSelect;
|
|
|
- boolean useCache = isSelect;
|
|
|
-
|
|
|
- KeyGenerator keyGenerator;
|
|
|
- String keyProperty = "id";
|
|
|
- String keyColumn = null;
|
|
|
- if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
|
|
|
- // first check for SelectKey annotation - that overrides
|
|
|
- // everything else
|
|
|
- SelectKey selectKey = method.getAnnotation(SelectKey.class);
|
|
|
- if (selectKey != null) {
|
|
|
- keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method),
|
|
|
- languageDriver);
|
|
|
- keyProperty = selectKey.keyProperty();
|
|
|
- } else if (options == null) {
|
|
|
- keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
|
|
|
- } else {
|
|
|
- keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
|
|
|
- keyProperty = options.keyProperty();
|
|
|
- keyColumn = options.keyColumn();
|
|
|
- }
|
|
|
- } else {
|
|
|
- keyGenerator = new NoKeyGenerator();
|
|
|
- }
|
|
|
-
|
|
|
- if (options != null) {
|
|
|
- if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
|
|
|
- flushCache = true;
|
|
|
- } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
|
|
|
- flushCache = false;
|
|
|
- }
|
|
|
- useCache = options.useCache();
|
|
|
- fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; // issue
|
|
|
- // #348
|
|
|
- timeout = options.timeout() > -1 ? options.timeout() : null;
|
|
|
- statementType = options.statementType();
|
|
|
- resultSetType = options.resultSetType();
|
|
|
- }
|
|
|
-
|
|
|
- String resultMapId = null;
|
|
|
- ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
|
|
|
- if (resultMapAnnotation != null) {
|
|
|
- String[] resultMaps = resultMapAnnotation.value();
|
|
|
- StringBuilder sb = new StringBuilder();
|
|
|
- for (String resultMap : resultMaps) {
|
|
|
- if (sb.length() > 0) {
|
|
|
- sb.append(",");
|
|
|
- }
|
|
|
- sb.append(resultMap);
|
|
|
- }
|
|
|
- resultMapId = sb.toString();
|
|
|
- } else if (isSelect) {
|
|
|
- resultMapId = parseResultMap(method);
|
|
|
- }
|
|
|
-
|
|
|
- assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
|
|
|
- // ParameterMapID
|
|
|
- null, parameterTypeClass, resultMapId, getReturnType(method), resultSetType, flushCache, useCache,
|
|
|
- // TODO gcode issue #577
|
|
|
- false, keyGenerator, keyProperty, keyColumn,
|
|
|
- // DatabaseID
|
|
|
- null, languageDriver,
|
|
|
- // ResultSets
|
|
|
- options != null ? nullOrEmpty(options.resultSets()) : null);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private LanguageDriver getLanguageDriver(Method method) {
|
|
|
- Lang lang = method.getAnnotation(Lang.class);
|
|
|
- Class<?> langClass = null;
|
|
|
- if (lang != null) {
|
|
|
- langClass = lang.value();
|
|
|
- }
|
|
|
- return assistant.getLanguageDriver(langClass);
|
|
|
- }
|
|
|
-
|
|
|
- private Class<?> getParameterType(Method method) {
|
|
|
- Class<?> parameterType = null;
|
|
|
- Class<?>[] parameterTypes = method.getParameterTypes();
|
|
|
- for (Class<?> currentParameterType : parameterTypes) {
|
|
|
- if (!RowBounds.class.isAssignableFrom(currentParameterType)
|
|
|
- && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
|
|
|
- if (parameterType == null) {
|
|
|
- parameterType = currentParameterType;
|
|
|
- } else {
|
|
|
- // issue #135
|
|
|
- parameterType = ParamMap.class;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return parameterType;
|
|
|
- }
|
|
|
-
|
|
|
- private Class<?> getReturnType(Method method) {
|
|
|
- Class<?> returnType = method.getReturnType();
|
|
|
- Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
|
|
|
- if (resolvedReturnType instanceof Class) {
|
|
|
- returnType = (Class<?>) resolvedReturnType;
|
|
|
- if (returnType.isArray()) {
|
|
|
- returnType = returnType.getComponentType();
|
|
|
- }
|
|
|
- // gcode issue #508
|
|
|
- if (void.class.equals(returnType)) {
|
|
|
- ResultType rt = method.getAnnotation(ResultType.class);
|
|
|
- if (rt != null) {
|
|
|
- returnType = rt.value();
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (resolvedReturnType instanceof ParameterizedType) {
|
|
|
- ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
|
|
|
- Class<?> rawType = (Class<?>) parameterizedType.getRawType();
|
|
|
- if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
|
|
|
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
|
|
- if (actualTypeArguments != null && actualTypeArguments.length == 1) {
|
|
|
- Type returnTypeParameter = actualTypeArguments[0];
|
|
|
- if (returnTypeParameter instanceof Class<?>) {
|
|
|
- returnType = (Class<?>) returnTypeParameter;
|
|
|
- } else if (returnTypeParameter instanceof ParameterizedType) {
|
|
|
- // (gcode issue #443) actual type can be a also a
|
|
|
- // parameterized type
|
|
|
- returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
|
|
|
- } else if (returnTypeParameter instanceof GenericArrayType) {
|
|
|
- Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
|
|
|
- // (gcode issue #525) support List<byte[]>
|
|
|
- returnType = Array.newInstance(componentType, 0).getClass();
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
|
|
|
- // (gcode issue 504) Do not look into Maps if there is not
|
|
|
- // MapKey annotation
|
|
|
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
|
|
- if (actualTypeArguments != null && actualTypeArguments.length == 2) {
|
|
|
- Type returnTypeParameter = actualTypeArguments[1];
|
|
|
- if (returnTypeParameter instanceof Class<?>) {
|
|
|
- returnType = (Class<?>) returnTypeParameter;
|
|
|
- } else if (returnTypeParameter instanceof ParameterizedType) {
|
|
|
- // (gcode issue 443) actual type can be a also a
|
|
|
- // parameterized type
|
|
|
- returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return returnType;
|
|
|
- }
|
|
|
-
|
|
|
- private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
|
|
|
- try {
|
|
|
- Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
|
|
|
- Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
|
|
|
- if (sqlAnnotationType != null) {
|
|
|
- if (sqlProviderAnnotationType != null) {
|
|
|
- throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named "
|
|
|
- + method.getName());
|
|
|
- }
|
|
|
- Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
|
|
|
- final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
|
|
|
- return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
|
|
|
- } else if (sqlProviderAnnotationType != null) {
|
|
|
- Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
|
|
|
- return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
|
|
|
- }
|
|
|
- return null;
|
|
|
- } catch (Exception e) {
|
|
|
- throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
|
|
|
- final StringBuilder sql = new StringBuilder();
|
|
|
- for (String fragment : strings) {
|
|
|
- sql.append(fragment);
|
|
|
- sql.append(" ");
|
|
|
- }
|
|
|
- return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
|
|
|
- }
|
|
|
-
|
|
|
- private SqlCommandType getSqlCommandType(Method method) {
|
|
|
- Class<? extends Annotation> type = getSqlAnnotationType(method);
|
|
|
-
|
|
|
- if (type == null) {
|
|
|
- type = getSqlProviderAnnotationType(method);
|
|
|
-
|
|
|
- if (type == null) {
|
|
|
- return SqlCommandType.UNKNOWN;
|
|
|
- }
|
|
|
-
|
|
|
- if (type == SelectProvider.class) {
|
|
|
- type = Select.class;
|
|
|
- } else if (type == InsertProvider.class) {
|
|
|
- type = Insert.class;
|
|
|
- } else if (type == UpdateProvider.class) {
|
|
|
- type = Update.class;
|
|
|
- } else if (type == DeleteProvider.class) {
|
|
|
- type = Delete.class;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
|
|
|
- }
|
|
|
-
|
|
|
- private Class<? extends Annotation> getSqlAnnotationType(Method method) {
|
|
|
- return chooseAnnotationType(method, sqlAnnotationTypes);
|
|
|
- }
|
|
|
-
|
|
|
- private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
|
|
|
- return chooseAnnotationType(method, sqlProviderAnnotationTypes);
|
|
|
- }
|
|
|
-
|
|
|
- private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
|
|
|
- for (Class<? extends Annotation> type : types) {
|
|
|
- Annotation annotation = method.getAnnotation(type);
|
|
|
- if (annotation != null) {
|
|
|
- return type;
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
|
|
|
- for (Result result : results) {
|
|
|
- List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
- if (result.id()) {
|
|
|
- flags.add(ResultFlag.ID);
|
|
|
- }
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) ((result.typeHandler() == UnknownTypeHandler.class) ? null
|
|
|
- : result.typeHandler());
|
|
|
- ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(result.property()),
|
|
|
- nullOrEmpty(result.column()), result.javaType() == void.class ? null : result.javaType(),
|
|
|
- result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
|
|
|
- hasNestedSelect(result) ? nestedSelectId(result) : null, null, null, null, typeHandler, flags, null, null,
|
|
|
- isLazy(result));
|
|
|
- resultMappings.add(resultMapping);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private String nestedSelectId(Result result) {
|
|
|
- String nestedSelect = result.one().select();
|
|
|
- if (nestedSelect.length() < 1) {
|
|
|
- nestedSelect = result.many().select();
|
|
|
- }
|
|
|
- if (!nestedSelect.contains(".")) {
|
|
|
- nestedSelect = type.getName() + "." + nestedSelect;
|
|
|
- }
|
|
|
- return nestedSelect;
|
|
|
- }
|
|
|
-
|
|
|
- private boolean isLazy(Result result) {
|
|
|
- boolean isLazy = configuration.isLazyLoadingEnabled();
|
|
|
- if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
|
|
|
- isLazy = (result.one().fetchType() == FetchType.LAZY);
|
|
|
- } else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
|
|
|
- isLazy = (result.many().fetchType() == FetchType.LAZY);
|
|
|
- }
|
|
|
- return isLazy;
|
|
|
- }
|
|
|
-
|
|
|
- private boolean hasNestedSelect(Result result) {
|
|
|
- if (result.one().select().length() > 0 && result.many().select().length() > 0) {
|
|
|
- throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
|
|
|
- }
|
|
|
- return result.one().select().length() > 0 || result.many().select().length() > 0;
|
|
|
- }
|
|
|
-
|
|
|
- private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
|
|
|
- for (Arg arg : args) {
|
|
|
- List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
- flags.add(ResultFlag.CONSTRUCTOR);
|
|
|
- if (arg.id()) {
|
|
|
- flags.add(ResultFlag.ID);
|
|
|
- }
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (arg.typeHandler() == UnknownTypeHandler.class ? null
|
|
|
- : arg.typeHandler());
|
|
|
- ResultMapping resultMapping = assistant.buildResultMapping(resultType, null, nullOrEmpty(arg.column()),
|
|
|
- arg.javaType() == void.class ? null : arg.javaType(),
|
|
|
- arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(), nullOrEmpty(arg.select()),
|
|
|
- nullOrEmpty(arg.resultMap()), null, null, typeHandler, flags, null, null, false);
|
|
|
- resultMappings.add(resultMapping);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private String nullOrEmpty(String value) {
|
|
|
- return value == null || value.trim().length() == 0 ? null : value;
|
|
|
- }
|
|
|
-
|
|
|
- private Result[] resultsIf(Results results) {
|
|
|
- return results == null ? new Result[0] : results.value();
|
|
|
- }
|
|
|
-
|
|
|
- private Arg[] argsIf(ConstructorArgs args) {
|
|
|
- return args == null ? new Arg[0] : args.value();
|
|
|
- }
|
|
|
-
|
|
|
- private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId,
|
|
|
- Class<?> parameterTypeClass, LanguageDriver languageDriver) {
|
|
|
- String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
|
|
|
- Class<?> resultTypeClass = selectKeyAnnotation.resultType();
|
|
|
- StatementType statementType = selectKeyAnnotation.statementType();
|
|
|
- String keyProperty = selectKeyAnnotation.keyProperty();
|
|
|
- String keyColumn = selectKeyAnnotation.keyColumn();
|
|
|
- boolean executeBefore = selectKeyAnnotation.before();
|
|
|
-
|
|
|
- // defaults
|
|
|
- boolean useCache = false;
|
|
|
- KeyGenerator keyGenerator = new NoKeyGenerator();
|
|
|
- Integer fetchSize = null;
|
|
|
- Integer timeout = null;
|
|
|
- boolean flushCache = false;
|
|
|
- String parameterMap = null;
|
|
|
- String resultMap = null;
|
|
|
- ResultSetType resultSetTypeEnum = null;
|
|
|
-
|
|
|
- SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
|
|
|
- SqlCommandType sqlCommandType = SqlCommandType.SELECT;
|
|
|
-
|
|
|
- assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
|
|
|
- parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false, keyGenerator,
|
|
|
- keyProperty, keyColumn, null, languageDriver, null);
|
|
|
-
|
|
|
- id = assistant.applyCurrentNamespace(id, false);
|
|
|
-
|
|
|
- MappedStatement keyStatement = configuration.getMappedStatement(id, false);
|
|
|
- SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
|
|
|
- configuration.addKeyGenerator(id, answer);
|
|
|
- return answer;
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String nullOrEmpty(String value) {
|
|
|
+ return value == null || value.trim().length() == 0 ? null : value;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Result[] resultsIf(Results results) {
|
|
|
+ return results == null ? new Result[0] : results.value();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Arg[] argsIf(ConstructorArgs args) {
|
|
|
+ return args == null ? new Arg[0] : args.value();
|
|
|
+ }
|
|
|
+
|
|
|
+ private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId,
|
|
|
+ Class<?> parameterTypeClass, LanguageDriver languageDriver) {
|
|
|
+ String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
|
|
|
+ Class<?> resultTypeClass = selectKeyAnnotation.resultType();
|
|
|
+ StatementType statementType = selectKeyAnnotation.statementType();
|
|
|
+ String keyProperty = selectKeyAnnotation.keyProperty();
|
|
|
+ String keyColumn = selectKeyAnnotation.keyColumn();
|
|
|
+ boolean executeBefore = selectKeyAnnotation.before();
|
|
|
+ // defaults
|
|
|
+ boolean useCache = false;
|
|
|
+ KeyGenerator keyGenerator = new NoKeyGenerator();
|
|
|
+ Integer fetchSize = null;
|
|
|
+ Integer timeout = null;
|
|
|
+ boolean flushCache = false;
|
|
|
+ String parameterMap = null;
|
|
|
+ String resultMap = null;
|
|
|
+ ResultSetType resultSetTypeEnum = null;
|
|
|
+ SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
|
|
|
+ SqlCommandType sqlCommandType = SqlCommandType.SELECT;
|
|
|
+ assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
|
|
|
+ parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false, keyGenerator,
|
|
|
+ keyProperty, keyColumn, null, languageDriver, null);
|
|
|
+ id = assistant.applyCurrentNamespace(id, false);
|
|
|
+ MappedStatement keyStatement = configuration.getMappedStatement(id, false);
|
|
|
+ SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
|
|
|
+ configuration.addKeyGenerator(id, answer);
|
|
|
+ return answer;
|
|
|
+ }
|
|
|
|
|
|
}
|