diff --git a/build.gradle b/build.gradle index 427aa0929b..1ff25ac789 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ ext { ] libraries = [ - mybatisVersion = '3.5.15', + mybatisVersion = '3.5.16', mybatisSpringVersion = '2.1.2', mybatisSpringBootStarterVersion = '2.3.1', springVersion = '5.3.27', diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java index f088f04729..c46243fe51 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java @@ -42,9 +42,9 @@ import org.apache.ibatis.type.TypeHandler; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -366,15 +366,16 @@ public Executor newExecutor(Transaction transaction, ExecutorType executorType) // Slow but a one time cost. A better solution is welcome. @Override - protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) { + public void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) { if (rm.hasNestedResultMaps()) { - for (Map.Entry entry : resultMaps.entrySet()) { - Object value = entry.getValue(); - if (value instanceof ResultMap) { - ResultMap entryResultMap = (ResultMap) value; + final String resultMapId = rm.getId(); + for (Object resultMapObject : resultMaps.values()) { + if (resultMapObject instanceof ResultMap) { + ResultMap entryResultMap = (ResultMap) resultMapObject; if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) { - Collection discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values(); - if (discriminatedResultMapNames.contains(rm.getId())) { + Collection discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap() + .values(); + if (discriminatedResultMapNames.contains(resultMapId)) { entryResultMap.forceNestedResultMaps(); } } @@ -387,8 +388,7 @@ protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) { @Override protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) { if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) { - for (Map.Entry entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) { - String discriminatedResultMapName = entry.getValue(); + for (String discriminatedResultMapName : rm.getDiscriminator().getDiscriminatorMap().values()) { if (hasResultMap(discriminatedResultMapName)) { ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName); if (discriminatedResultMap.hasNestedResultMaps()) { @@ -400,17 +400,32 @@ protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) { } } - protected class StrictMap extends HashMap { + protected class StrictMap extends ConcurrentHashMap { private static final long serialVersionUID = -4950446264854982944L; private final String name; private BiFunction conflictMessageProducer; + public StrictMap(String name, int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + this.name = name; + } + + public StrictMap(String name, int initialCapacity) { + super(initialCapacity); + this.name = name; + } + public StrictMap(String name) { super(); this.name = name; } + public StrictMap(String name, Map m) { + super(m); + this.name = name; + } + /** * Assign a function for producing a conflict error message when contains value with the same key. *

@@ -445,6 +460,15 @@ public V put(String key, V value) { return super.put(key, value); } + @Override + public boolean containsKey(Object key) { + if (key == null) { + return false; + } + + return super.get(key) != null; + } + @Override public V get(Object key) { V value = super.get(key); diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java index b43444323b..737d80d54c 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java @@ -20,7 +20,6 @@ import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.baomidou.mybatisplus.core.toolkit.StringPool; -import lombok.Getter; import org.apache.ibatis.annotations.*; import org.apache.ibatis.annotations.ResultMap; import org.apache.ibatis.annotations.Options.FlushCachePolicy; @@ -29,7 +28,6 @@ import org.apache.ibatis.builder.CacheRefResolver; import org.apache.ibatis.builder.IncompleteElementException; 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.cursor.Cursor; import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; @@ -121,32 +119,18 @@ public void parse() { configuration.addIncompleteMethod(new InjectorResolver(this)); } } - parsePendingMethods(); + configuration.parsePendingMethods(false); } void parserInjector() { GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type); } - private boolean canHaveStatement(Method method) { + private static boolean canHaveStatement(Method method) { // issue #237 return !method.isBridge() && !method.isDefault(); } - private void parsePendingMethods() { - Collection incompleteMethods = configuration.getIncompleteMethods(); - synchronized (incompleteMethods) { - Iterator 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 @@ -177,7 +161,8 @@ private void parseCache() { Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size(); Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval(); Properties props = convertToProperties(cacheDomain.properties()); - assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props); + assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, + cacheDomain.readWrite(), cacheDomain.blocking(), props); } } @@ -187,8 +172,7 @@ private Properties convertToProperties(Property[] properties) { } Properties props = new Properties(); for (Property property : properties) { - props.setProperty(property.name(), - PropertyParser.parse(property.value(), configuration.getVariables())); + props.setProperty(property.name(), PropertyParser.parse(property.value(), configuration.getVariables())); } return props; } @@ -204,7 +188,7 @@ private void parseCacheRef() { if (refType != void.class && !refName.isEmpty()) { throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef"); } - String namespace = (refType != void.class) ? refType.getName() : refName; + String namespace = refType != void.class ? refType.getName() : refName; try { assistant.useCacheRef(namespace); } catch (IncompleteElementException e) { @@ -214,7 +198,7 @@ private void parseCacheRef() { } private String parseResultMap(Method method) { - Class returnType = getReturnType(method); + Class returnType = getReturnType(method, type); Arg[] args = method.getAnnotationsByType(Arg.class); Result[] results = method.getAnnotationsByType(Result.class); TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class); @@ -226,24 +210,26 @@ private String parseResultMap(Method method) { private String generateResultMapName(Method method) { Results results = method.getAnnotation(Results.class); if (results != null && !results.id().isEmpty()) { - return type.getName() + StringPool.DOT + results.id(); + return type.getName() + "." + results.id(); } StringBuilder suffix = new StringBuilder(); for (Class c : method.getParameterTypes()) { - suffix.append(StringPool.DASH); + suffix.append("-"); suffix.append(c.getSimpleName()); } if (suffix.length() < 1) { suffix.append("-void"); } - return type.getName() + StringPool.DOT + method.getName() + suffix; + return type.getName() + "." + method.getName() + suffix; } - private void applyResultMap(String resultMapId, Class returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) { + private void applyResultMap(String resultMapId, Class returnType, Arg[] args, Result[] results, + TypeDiscriminator discriminator) { List resultMappings = new ArrayList<>(); 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); } @@ -251,11 +237,12 @@ private void applyResultMap(String resultMapId, Class returnType, Arg[] args, private void createDiscriminatorResultMaps(String resultMapId, Class resultType, TypeDiscriminator discriminator) { if (discriminator != null) { for (Case c : discriminator.cases()) { - String caseResultMapId = resultMapId + StringPool.DASH + c.value(); + String caseResultMapId = resultMapId + "-" + c.value(); List resultMappings = new ArrayList<>(); // issue #136 applyConstructorArgs(c.constructArgs(), resultType, resultMappings); applyResults(c.results(), resultType, resultMappings); + // TODO add AutoMappingBehaviour assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null); } } @@ -267,13 +254,13 @@ private Discriminator applyDiscriminator(String resultMapId, Class resultType Class javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType(); JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType(); @SuppressWarnings("unchecked") - Class> typeHandler = (Class>) - (discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler()); + Class> typeHandler = (Class>) (discriminator + .typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler()); Case[] cases = discriminator.cases(); Map discriminatorMap = new HashMap<>(); for (Case c : cases) { String value = c.value(); - String caseResultMapId = resultMapId + StringPool.DASH + value; + String caseResultMapId = resultMapId + "-" + value; discriminatorMap.put(value, caseResultMapId); } return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap); @@ -286,19 +273,23 @@ void parseStatement(Method method) { final LanguageDriver languageDriver = getLanguageDriver(method); getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> { - final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method); + final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, + languageDriver, method); final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType(); - final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()).orElse(null); - final String mappedStatementId = type.getName() + StringPool.DOT + method.getName(); + final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()) + .orElse(null); + final String mappedStatementId = type.getName() + "." + method.getName(); final KeyGenerator keyGenerator; String keyProperty = null; String keyColumn = null; if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { // first check for SelectKey annotation - that overrides everything else - SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey) x.getAnnotation()).orElse(null); + SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class) + .map(x -> (SelectKey) x.getAnnotation()).orElse(null); if (selectKey != null) { - keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver); + keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), + languageDriver); keyProperty = selectKey.keyProperty(); } else if (options == null) { keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; @@ -325,7 +316,8 @@ void parseStatement(Method method) { flushCache = false; } useCache = options.useCache(); - fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348 + // issue #348 + fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; timeout = options.timeout() > -1 ? options.timeout() : null; statementType = options.statementType(); if (options.resultSetType() != ResultSetType.DEFAULT) { @@ -337,35 +329,19 @@ void parseStatement(Method method) { if (isSelect) { ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class); if (resultMapAnnotation != null) { - resultMapId = String.join(StringPool.COMMA, resultMapAnnotation.value()); + resultMapId = String.join(",", resultMapAnnotation.value()); } else { resultMapId = generateResultMapName(method); } } - assistant.addMappedStatement( - mappedStatementId, - sqlSource, - statementType, - sqlCommandType, - fetchSize, - timeout, + assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout, // ParameterMapID - null, - parameterTypeClass, - resultMapId, - getReturnType(method), - resultSetType, - flushCache, - useCache, - false, - keyGenerator, - keyProperty, - keyColumn, - statementAnnotation.getDatabaseId(), - languageDriver, + null, parameterTypeClass, resultMapId, getReturnType(method, type), resultSetType, flushCache, useCache, + // TODO gcode issue #577 + false, keyGenerator, keyProperty, keyColumn, statementAnnotation.getDatabaseId(), languageDriver, // ResultSets - options != null ? nullOrEmpty(options.resultSets()) : null); + options != null ? nullOrEmpty(options.resultSets()) : null, statementAnnotation.isDirtySelect()); }); } @@ -382,7 +358,8 @@ 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 (!RowBounds.class.isAssignableFrom(currentParameterType) + && !ResultHandler.class.isAssignableFrom(currentParameterType)) { if (parameterType == null) { parameterType = currentParameterType; } else { @@ -394,7 +371,7 @@ private Class getParameterType(Method method) { return parameterType; } - private Class getReturnType(Method method) { + private static Class getReturnType(Method method, Class type) { Class returnType = method.getReturnType(); Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type); if (resolvedReturnType instanceof Class) { @@ -467,24 +444,15 @@ private void applyResults(Result[] results, Class resultType, List> typeHandler = (Class>) - ((result.typeHandler() == UnknownTypeHandler.class) ? null : result.typeHandler()); + Class> typeHandler = (Class>) (result + .typeHandler() == UnknownTypeHandler.class ? null : result.typeHandler()); boolean hasNestedResultMap = hasNestedResultMap(result); - ResultMapping resultMapping = assistant.buildResultMapping( - resultType, - nullOrEmpty(result.property()), - nullOrEmpty(result.column()), - result.javaType() == void.class ? null : result.javaType(), + 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, - hasNestedResultMap ? nestedResultMapId(result) : null, - null, - hasNestedResultMap ? findColumnPrefix(result) : null, - typeHandler, - flags, - null, - null, - isLazy(result)); + hasNestedResultMap ? nestedResultMapId(result) : null, null, + hasNestedResultMap ? findColumnPrefix(result) : null, typeHandler, flags, null, null, isLazy(result)); resultMappings.add(resultMapping); } } @@ -551,23 +519,12 @@ private void applyConstructorArgs(Arg[] args, Class resultType, List> typeHandler = (Class>) - (arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler()); - ResultMapping resultMapping = assistant.buildResultMapping( - resultType, - nullOrEmpty(arg.name()), - nullOrEmpty(arg.column()), - arg.javaType() == void.class ? null : arg.javaType(), - arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(), - nullOrEmpty(arg.select()), - nullOrEmpty(arg.resultMap()), - null, - nullOrEmpty(arg.columnPrefix()), - typeHandler, - flags, - null, - null, - false); + Class> typeHandler = (Class>) (arg + .typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler()); + ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(arg.name()), + nullOrEmpty(arg.column()), arg.javaType() == void.class ? null : arg.javaType(), + arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(), nullOrEmpty(arg.select()), + nullOrEmpty(arg.resultMap()), null, nullOrEmpty(arg.columnPrefix()), typeHandler, flags, null, null, false); resultMappings.add(resultMapping); } } @@ -576,7 +533,8 @@ private String nullOrEmpty(String value) { return value == null || value.trim().length() == 0 ? null : value; } - private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class parameterTypeClass, LanguageDriver languageDriver) { + 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(); @@ -598,9 +556,9 @@ private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, St SqlSource sqlSource = buildSqlSource(selectKeyAnnotation, parameterTypeClass, languageDriver, null); SqlCommandType sqlCommandType = SqlCommandType.SELECT; - assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, - flushCache, useCache, false, - keyGenerator, keyProperty, keyColumn, databaseId, languageDriver, null); + assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, + parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false, keyGenerator, + keyProperty, keyColumn, databaseId, languageDriver, null, false); id = assistant.applyCurrentNamespace(id, false); @@ -614,7 +572,8 @@ private SqlSource buildSqlSource(Annotation annotation, Class parameterType, Method method) { if (annotation instanceof Select) { return buildSqlSourceFromStrings(((Select) annotation).value(), parameterType, languageDriver); - } else if (annotation instanceof Update) { + } + if (annotation instanceof Update) { return buildSqlSourceFromStrings(((Update) annotation).value(), parameterType, languageDriver); } else if (annotation instanceof Insert) { return buildSqlSourceFromStrings(((Insert) annotation).value(), parameterType, languageDriver); @@ -633,48 +592,65 @@ private SqlSource buildSqlSourceFromStrings(String[] strings, Class parameter @SafeVarargs private final Optional getAnnotationWrapper(Method method, boolean errorIfNoMatch, - Class... targetTypes) { + Class... targetTypes) { return getAnnotationWrapper(method, errorIfNoMatch, Arrays.asList(targetTypes)); } private Optional getAnnotationWrapper(Method method, boolean errorIfNoMatch, - Collection> targetTypes) { + Collection> targetTypes) { String databaseId = configuration.getDatabaseId(); Map statementAnnotations = targetTypes.stream() .flatMap(x -> Arrays.stream(method.getAnnotationsByType(x))).map(AnnotationWrapper::new) .collect(Collectors.toMap(AnnotationWrapper::getDatabaseId, x -> x, (existing, duplicate) -> { - throw new BuilderException(String.format("Detected conflicting annotations '%s' and '%s' on '%s'.", - existing.getAnnotation(), duplicate.getAnnotation(), - method.getDeclaringClass().getName() + StringPool.DOT + method.getName())); + throw new BuilderException( + String.format("Detected conflicting annotations '%s' and '%s' on '%s'.", existing.getAnnotation(), + duplicate.getAnnotation(), method.getDeclaringClass().getName() + "." + method.getName())); })); AnnotationWrapper annotationWrapper = null; if (databaseId != null) { annotationWrapper = statementAnnotations.get(databaseId); } if (annotationWrapper == null) { - annotationWrapper = statementAnnotations.get(StringPool.EMPTY); + annotationWrapper = statementAnnotations.get(""); } if (errorIfNoMatch && annotationWrapper == null && !statementAnnotations.isEmpty()) { // Annotations exist, but there is no matching one for the specified databaseId - throw new BuilderException( - String.format( - "Could not find a statement annotation that correspond a current database or default statement on method '%s.%s'. Current database id is [%s].", - method.getDeclaringClass().getName(), method.getName(), databaseId)); + throw new BuilderException(String.format( + "Could not find a statement annotation that correspond a current database or default statement on method '%s.%s'. Current database id is [%s].", + method.getDeclaringClass().getName(), method.getName(), databaseId)); } return Optional.ofNullable(annotationWrapper); } - @Getter - private class AnnotationWrapper { + public static Class getMethodReturnType(String mapperFqn, String localStatementId) { + if (mapperFqn == null || localStatementId == null) { + return null; + } + try { + Class mapperClass = Resources.classForName(mapperFqn); + for (Method method : mapperClass.getMethods()) { + if (method.getName().equals(localStatementId) && canHaveStatement(method)) { + return getReturnType(method, mapperClass); + } + } + } catch (ClassNotFoundException e) { + // No corresponding mapper interface which is OK + } + return null; + } + + private static class AnnotationWrapper { private final Annotation annotation; private final String databaseId; private final SqlCommandType sqlCommandType; + private boolean dirtySelect; AnnotationWrapper(Annotation annotation) { this.annotation = annotation; if (annotation instanceof Select) { databaseId = ((Select) annotation).databaseId(); sqlCommandType = SqlCommandType.SELECT; + dirtySelect = ((Select) annotation).affectData(); } else if (annotation instanceof Update) { databaseId = ((Update) annotation).databaseId(); sqlCommandType = SqlCommandType.UPDATE; @@ -687,6 +663,7 @@ private class AnnotationWrapper { } else if (annotation instanceof SelectProvider) { databaseId = ((SelectProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.SELECT; + dirtySelect = ((SelectProvider) annotation).affectData(); } else if (annotation instanceof UpdateProvider) { databaseId = ((UpdateProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.UPDATE; @@ -707,5 +684,21 @@ private class AnnotationWrapper { } } } + + Annotation getAnnotation() { + return annotation; + } + + SqlCommandType getSqlCommandType() { + return sqlCommandType; + } + + String getDatabaseId() { + return databaseId; + } + + boolean isDirtySelect() { + return dirtySelect; + } } } diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java index 1a778ab16d..9bccfb43bc 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java @@ -54,7 +54,7 @@ public ResultMapping buildResultMapping(Class resultType, String property, St boolean lazy) { Class javaTypeClass = resolveResultJavaType(resultType, property, javaType); TypeHandler typeHandlerInstance = null; - if (typeHandler != null && typeHandler != UnknownTypeHandler.class) { + if (typeHandler != null) { if (IJsonTypeHandler.class.isAssignableFrom(typeHandler)) { try { Field field = resultType.getDeclaredField(property); diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperRegistry.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperRegistry.java index e413104bcc..f44e4fd97a 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperRegistry.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperRegistry.java @@ -65,7 +65,7 @@ public boolean hasMapper(Class type) { return knownMappers.containsKey(type); } - /** + /** * 清空 Mapper 缓存信息 */ protected void removeMapper(Class type) { diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java index 62496ff40d..56b2ba6c96 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java @@ -18,6 +18,7 @@ import org.apache.ibatis.builder.BaseBuilder; import org.apache.ibatis.builder.BuilderException; import org.apache.ibatis.builder.xml.XMLConfigBuilder; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; import org.apache.ibatis.datasource.DataSourceFactory; import org.apache.ibatis.executor.ErrorContext; @@ -57,6 +58,7 @@ public class MybatisXMLConfigBuilder extends BaseBuilder { private String environment; private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); + public MybatisXMLConfigBuilder(Reader reader) { this(reader, null, null); } @@ -66,7 +68,12 @@ public MybatisXMLConfigBuilder(Reader reader, String environment) { } public MybatisXMLConfigBuilder(Reader reader, String environment, Properties props) { - this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); + this(MybatisConfiguration.class, reader, environment, props); + } + + public MybatisXMLConfigBuilder(Class configClass, Reader reader, String environment, + Properties props) { + this(configClass, new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } public MybatisXMLConfigBuilder(InputStream inputStream) { @@ -78,11 +85,17 @@ public MybatisXMLConfigBuilder(InputStream inputStream, String environment) { } public MybatisXMLConfigBuilder(InputStream inputStream, String environment, Properties props) { - this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); + this(MybatisConfiguration.class, inputStream, environment, props); } - private MybatisXMLConfigBuilder(XPathParser parser, String environment, Properties props) { - super(new MybatisConfiguration()); + public MybatisXMLConfigBuilder(Class configClass, InputStream inputStream, String environment, + Properties props) { + this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); + } + + private MybatisXMLConfigBuilder(Class configClass, XPathParser parser, String environment, + Properties props) { + super(newConfig(configClass)); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; @@ -104,10 +117,10 @@ private void parseConfiguration(XNode root) { // issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); - loadCustomVfs(settings); + loadCustomVfsImpl(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases")); - pluginElement(root.evalNode("plugins")); + pluginsElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); @@ -115,8 +128,8 @@ private void parseConfiguration(XNode root) { // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); - typeHandlerElement(root.evalNode("typeHandlers")); - mapperElement(root.evalNode("mappers")); + typeHandlersElement(root.evalNode("typeHandlers")); + mappersElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } @@ -131,22 +144,24 @@ private Properties settingsAsProperties(XNode context) { MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { - throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); + throw new BuilderException( + "The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } return props; } - private void loadCustomVfs(Properties props) throws ClassNotFoundException { + private void loadCustomVfsImpl(Properties props) throws ClassNotFoundException { String value = props.getProperty("vfsImpl"); - if (value != null) { - String[] clazzes = value.split(","); - for (String clazz : clazzes) { - if (!clazz.isEmpty()) { - @SuppressWarnings("unchecked") - Class vfsImpl = (Class) Resources.classForName(clazz); - configuration.setVfsImpl(vfsImpl); - } + if (value == null) { + return; + } + String[] clazzes = value.split(","); + for (String clazz : clazzes) { + if (!clazz.isEmpty()) { + @SuppressWarnings("unchecked") + Class vfsImpl = (Class) Resources.classForName(clazz); + configuration.setVfsImpl(vfsImpl); } } } @@ -156,36 +171,38 @@ private void loadCustomLogImpl(Properties props) { configuration.setLogImpl(logImpl); } - private void typeAliasesElement(XNode parent) { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String typeAliasPackage = child.getStringAttribute("name"); - configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); - } else { - String alias = child.getStringAttribute("alias"); - String type = child.getStringAttribute("type"); - try { - Class clazz = Resources.classForName(type); - if (alias == null) { - typeAliasRegistry.registerAlias(clazz); - } else { - typeAliasRegistry.registerAlias(alias, clazz); - } - } catch (ClassNotFoundException e) { - throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); + private void typeAliasesElement(XNode context) { + if (context == null) { + return; + } + for (XNode child : context.getChildren()) { + if ("package".equals(child.getName())) { + String typeAliasPackage = child.getStringAttribute("name"); + configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); + } else { + String alias = child.getStringAttribute("alias"); + String type = child.getStringAttribute("type"); + try { + Class clazz = Resources.classForName(type); + if (alias == null) { + typeAliasRegistry.registerAlias(clazz); + } else { + typeAliasRegistry.registerAlias(alias, clazz); } + } catch (ClassNotFoundException e) { + throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); } } } } - private void pluginElement(XNode parent) throws Exception { - if (parent != null) { - for (XNode child : parent.getChildren()) { + private void pluginsElement(XNode context) throws Exception { + if (context != null) { + for (XNode child : context.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); - Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance(); + Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor() + .newInstance(); interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } @@ -219,25 +236,27 @@ private void reflectorFactoryElement(XNode context) throws Exception { } private void propertiesElement(XNode context) throws Exception { - if (context != null) { - Properties defaults = context.getChildrenAsProperties(); - String resource = context.getStringAttribute("resource"); - String url = context.getStringAttribute("url"); - if (resource != null && url != null) { - throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); - } - if (resource != null) { - defaults.putAll(Resources.getResourceAsProperties(resource)); - } else if (url != null) { - defaults.putAll(Resources.getUrlAsProperties(url)); - } - Properties vars = configuration.getVariables(); - if (vars != null) { - defaults.putAll(vars); - } - parser.setVariables(defaults); - configuration.setVariables(defaults); + if (context == null) { + return; + } + Properties defaults = context.getChildrenAsProperties(); + String resource = context.getStringAttribute("resource"); + String url = context.getStringAttribute("url"); + if (resource != null && url != null) { + throw new BuilderException( + "The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } + if (resource != null) { + defaults.putAll(Resources.getResourceAsProperties(resource)); + } else if (url != null) { + defaults.putAll(Resources.getUrlAsProperties(url)); + } + Properties vars = configuration.getVariables(); + if (vars != null) { + defaults.putAll(vars); + } + parser.setVariables(defaults); + configuration.setVariables(defaults); } private void settingsElement(Properties props) { @@ -270,45 +289,47 @@ private void settingsElement(Properties props) { configuration.setArgNameBasedConstructorAutoMapping(booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false)); configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType"))); configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false)); + // TODO 这里改变了原生行为 configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), true)); ((MybatisConfiguration) configuration).setUseGeneratedShortKey(booleanValueOf(props.getProperty("useGeneratedShortKey"), true)); } private void environmentsElement(XNode context) throws Exception { - if (context != null) { - if (environment == null) { - environment = context.getStringAttribute("default"); - } - for (XNode child : context.getChildren()) { - String id = child.getStringAttribute("id"); - if (isSpecifiedEnvironment(id)) { - TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); - DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); - DataSource dataSource = dsFactory.getDataSource(); - Environment.Builder environmentBuilder = new Environment.Builder(id) - .transactionFactory(txFactory) - .dataSource(dataSource); - configuration.setEnvironment(environmentBuilder.build()); - break; - } + if (context == null) { + return; + } + if (environment == null) { + environment = context.getStringAttribute("default"); + } + for (XNode child : context.getChildren()) { + String id = child.getStringAttribute("id"); + if (isSpecifiedEnvironment(id)) { + TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); + DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); + DataSource dataSource = dsFactory.getDataSource(); + Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory) + .dataSource(dataSource); + configuration.setEnvironment(environmentBuilder.build()); + break; } } } private void databaseIdProviderElement(XNode context) throws Exception { - DatabaseIdProvider databaseIdProvider = null; - if (context != null) { - String type = context.getStringAttribute("type"); - // awful patch to keep backward compatibility - if ("VENDOR".equals(type)) { - type = "DB_VENDOR"; - } - Properties properties = context.getChildrenAsProperties(); - databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance(); - databaseIdProvider.setProperties(properties); + if (context == null) { + return; + } + String type = context.getStringAttribute("type"); + // awful patch to keep backward compatibility + if ("VENDOR".equals(type)) { + type = "DB_VENDOR"; } + Properties properties = context.getChildrenAsProperties(); + DatabaseIdProvider databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor() + .newInstance(); + databaseIdProvider.setProperties(properties); Environment environment = configuration.getEnvironment(); - if (environment != null && databaseIdProvider != null) { + if (environment != null) { String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); configuration.setDatabaseId(databaseId); } @@ -336,61 +357,66 @@ private DataSourceFactory dataSourceElement(XNode context) throws Exception { throw new BuilderException("Environment declaration requires a DataSourceFactory."); } - private void typeHandlerElement(XNode parent) { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String typeHandlerPackage = child.getStringAttribute("name"); - typeHandlerRegistry.register(typeHandlerPackage); - } else { - String javaTypeName = child.getStringAttribute("javaType"); - String jdbcTypeName = child.getStringAttribute("jdbcType"); - String handlerTypeName = child.getStringAttribute("handler"); - Class javaTypeClass = resolveClass(javaTypeName); - JdbcType jdbcType = resolveJdbcType(jdbcTypeName); - Class typeHandlerClass = resolveClass(handlerTypeName); - if (javaTypeClass != null) { - if (jdbcType == null) { - typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); - } else { - typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); - } + private void typeHandlersElement(XNode context) { + if (context == null) { + return; + } + for (XNode child : context.getChildren()) { + if ("package".equals(child.getName())) { + String typeHandlerPackage = child.getStringAttribute("name"); + typeHandlerRegistry.register(typeHandlerPackage); + } else { + String javaTypeName = child.getStringAttribute("javaType"); + String jdbcTypeName = child.getStringAttribute("jdbcType"); + String handlerTypeName = child.getStringAttribute("handler"); + Class javaTypeClass = resolveClass(javaTypeName); + JdbcType jdbcType = resolveJdbcType(jdbcTypeName); + Class typeHandlerClass = resolveClass(handlerTypeName); + if (javaTypeClass != null) { + if (jdbcType == null) { + typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { - typeHandlerRegistry.register(typeHandlerClass); + typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } + } else { + typeHandlerRegistry.register(typeHandlerClass); } } } } - private void mapperElement(XNode parent) throws Exception { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String mapperPackage = child.getStringAttribute("name"); - configuration.addMappers(mapperPackage); - } else { - String resource = child.getStringAttribute("resource"); - String url = child.getStringAttribute("url"); - String mapperClass = child.getStringAttribute("class"); - if (resource != null && url == null && mapperClass == null) { - ErrorContext.instance().resource(resource); - try (InputStream inputStream = Resources.getResourceAsStream(resource)) { - MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); - mapperParser.parse(); - } - } else if (resource == null && url != null && mapperClass == null) { - ErrorContext.instance().resource(url); - try (InputStream inputStream = Resources.getUrlAsStream(url)) { - MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); - mapperParser.parse(); - } - } else if (resource == null && url == null && mapperClass != null) { - Class mapperInterface = Resources.classForName(mapperClass); - configuration.addMapper(mapperInterface); - } else { - throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); + private void mappersElement(XNode context) throws Exception { + if (context == null) { + return; + } + for (XNode child : context.getChildren()) { + if ("package".equals(child.getName())) { + String mapperPackage = child.getStringAttribute("name"); + configuration.addMappers(mapperPackage); + } else { + String resource = child.getStringAttribute("resource"); + String url = child.getStringAttribute("url"); + String mapperClass = child.getStringAttribute("class"); + if (resource != null && url == null && mapperClass == null) { + ErrorContext.instance().resource(resource); + try (InputStream inputStream = Resources.getResourceAsStream(resource)) { + XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, + configuration.getSqlFragments()); + mapperParser.parse(); + } + } else if (resource == null && url != null && mapperClass == null) { + ErrorContext.instance().resource(url); + try (InputStream inputStream = Resources.getUrlAsStream(url)) { + XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, + configuration.getSqlFragments()); + mapperParser.parse(); } + } else if (resource == null && url == null && mapperClass != null) { + Class mapperInterface = Resources.classForName(mapperClass); + configuration.addMapper(mapperInterface); + } else { + throw new BuilderException( + "A mapper element may only specify a url, resource or class, but not more than one."); } } } @@ -405,4 +431,13 @@ private boolean isSpecifiedEnvironment(String id) { } return environment.equals(id); } + + private static Configuration newConfig(Class configClass) { + try { + return configClass.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + throw new BuilderException("Failed to create a new Configuration instance.", ex); + } + } + } diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java index a60db5af28..d24633a9c6 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java @@ -19,6 +19,7 @@ import org.apache.ibatis.builder.BuilderException; import org.apache.ibatis.builder.CacheRefResolver; import org.apache.ibatis.builder.IncompleteElementException; +import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.builder.ResultMapResolver; import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; import org.apache.ibatis.builder.xml.XMLStatementBuilder; @@ -42,10 +43,8 @@ import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; @@ -59,7 +58,7 @@ public class MybatisXMLMapperBuilder extends BaseBuilder { private final XPathParser parser; - private final MybatisMapperBuilderAssistant builderAssistant; + private final MapperBuilderAssistant builderAssistant; private final Map sqlFragments; private final String resource; @@ -104,9 +103,9 @@ public void parse() { configuration.addLoadedResource(resource); bindMapperForNamespace(); } - parsePendingResultMaps(); - parsePendingCacheRefs(); - parsePendingStatements(); + configuration.parsePendingResultMaps(false); + configuration.parsePendingCacheRefs(false); + configuration.parsePendingStatements(false); } public XNode getSqlFragment(String refid) { @@ -150,51 +149,6 @@ private void buildStatementFromContext(List list, String requiredDatabase } } - private void parsePendingResultMaps() { - Collection incompleteResultMaps = configuration.getIncompleteResultMaps(); - synchronized (incompleteResultMaps) { - Iterator iter = incompleteResultMaps.iterator(); - while (iter.hasNext()) { - try { - iter.next().resolve(); - iter.remove(); - } catch (IncompleteElementException e) { - // ResultMap is still missing a resource... - } - } - } - } - - private void parsePendingCacheRefs() { - Collection incompleteCacheRefs = configuration.getIncompleteCacheRefs(); - synchronized (incompleteCacheRefs) { - Iterator iter = incompleteCacheRefs.iterator(); - while (iter.hasNext()) { - try { - iter.next().resolveCacheRef(); - iter.remove(); - } catch (IncompleteElementException e) { - // Cache ref is still missing a resource... - } - } - } - } - - private void parsePendingStatements() { - Collection incompleteStatements = configuration.getIncompleteStatements(); - synchronized (incompleteStatements) { - Iterator iter = incompleteStatements.iterator(); - while (iter.hasNext()) { - try { - iter.next().parseStatementNode(); - iter.remove(); - } catch (IncompleteElementException e) { - // Statement is still missing a resource... - } - } - } - } - private void cacheRefElement(XNode context) { if (context != null) { configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace")); diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java index e6d5f7e718..c2df7bc3c1 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java @@ -197,14 +197,13 @@ private Object convertToDeclaredCollection(Configuration config, List lis private Object convertToArray(List list) { Class arrayComponentType = method.getReturnType().getComponentType(); Object array = Array.newInstance(arrayComponentType, list.size()); - if (arrayComponentType.isPrimitive()) { - for (int i = 0; i < list.size(); i++) { - Array.set(array, i, list.get(i)); - } - return array; - } else { + if (!arrayComponentType.isPrimitive()) { return list.toArray((E[]) array); } + for (int i = 0; i < list.size(); i++) { + Array.set(array, i, list.get(i)); + } + return array; } private Map executeForMap(SqlSession sqlSession, Object[] args) { diff --git a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperProxy.java b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperProxy.java index 9bfe48abb0..65ba3f3815 100644 --- a/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperProxy.java +++ b/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperProxy.java @@ -15,10 +15,11 @@ */ package com.baomidou.mybatisplus.core.override; -import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.binding.MapperProxy; import org.apache.ibatis.reflection.ExceptionUtil; import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.util.MapUtil; import java.io.Serializable; import java.lang.invoke.MethodHandle; @@ -39,7 +40,7 @@ */ public class MybatisMapperProxy implements InvocationHandler, Serializable { - private static final long serialVersionUID = -5154982058833204559L; + private static final long serialVersionUID = -4724728412955527868L; private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC; private static final Constructor lookupConstructor; @@ -73,7 +74,7 @@ public MybatisMapperProxy(SqlSession sqlSession, Class mapperInterface, Map { - if (m.isDefault()) { - try { - if (privateLookupInMethod == null) { - return new DefaultMethodInvoker(getMethodHandleJava8(method)); - } else { - return new DefaultMethodInvoker(getMethodHandleJava9(method)); - } - } catch (IllegalAccessException | InstantiationException | InvocationTargetException - | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } else { + return MapUtil.computeIfAbsent(methodCache, method, m -> { + if (!m.isDefault()) { return new PlainMethodInvoker(new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } + try { + if (privateLookupInMethod == null) { + return new DefaultMethodInvoker(getMethodHandleJava8(method)); + } + return new DefaultMethodInvoker(getMethodHandleJava9(method)); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException + | NoSuchMethodException e) { + throw new RuntimeException(e); + } }); } catch (RuntimeException re) { Throwable cause = re.getCause(); @@ -143,7 +141,6 @@ private static class PlainMethodInvoker implements MapperMethodInvoker { private final MybatisMapperMethod mapperMethod; public PlainMethodInvoker(MybatisMapperMethod mapperMethod) { - super(); this.mapperMethod = mapperMethod; }