Browse Source

Merge branch 'github3.0' into 3.0

hubin 1 year ago
parent
commit
581380df14
44 changed files with 1714 additions and 90 deletions
  1. 1 1
      build.gradle
  2. 5 2
      changelog-temp.md
  3. 1 1
      gradle.properties
  4. 3 5
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java
  5. 131 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java
  6. 2 3
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java
  7. 451 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java
  8. 2 2
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractWrapper.java
  9. 14 2
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Join.java
  10. 8 6
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/AnnotationHandler.java
  11. 49 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/IJsonTypeHandler.java
  12. 14 7
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/TableFieldInfo.java
  13. 74 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/AnnotationUtils.java
  14. 52 0
      mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/MybatisUtils.java
  15. 70 0
      mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/metadata/TableInfoHelperTest.java
  16. 152 0
      mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/AnnotationUtilsTest.java
  17. 43 3
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/AbstractJsonTypeHandler.java
  18. 9 13
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/Fastjson2TypeHandler.java
  19. 11 9
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/FastjsonTypeHandler.java
  20. 12 11
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/GsonTypeHandler.java
  21. 18 11
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/JacksonTypeHandler.java
  22. 3 3
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/service/impl/ServiceImpl.java
  23. 2 2
      mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java
  24. 2 2
      mybatis-plus-extension/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/BaseDbTest.kt
  25. 2 2
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/BaseDbTest.java
  26. 1 1
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/MultiAopTest.java
  27. 67 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2Entity.java
  28. 10 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2EntityMapper.java
  29. 67 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntity.java
  30. 10 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntityMapper.java
  31. 56 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/Fastjson2Test.java
  32. 56 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastjsonTest.java
  33. 67 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntity.java
  34. 10 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntityMapper.java
  35. 57 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonTest.java
  36. 67 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntity.java
  37. 10 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntityMapper.java
  38. 56 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JacksonTest.java
  39. 22 0
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/Entity.java
  40. 21 1
      mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/ResultMapTest.java
  41. 3 0
      mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/resultmap/EntityMapper.xml
  42. 1 1
      spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java
  43. 1 1
      spring-boot-starter/mybatis-plus-spring-boot3-starter/build.gradle
  44. 1 1
      spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java

+ 1 - 1
build.gradle

@@ -17,7 +17,7 @@ ext {
         springVersion = '5.3.27',
         springBootVersion = '2.7.15',
         springBoot3Version = '3.2.0',
-        springCloudVersion = '3.1.1',
+        springCloudVersion = '3.1.8',
         jsqlparserVersion = '4.6',
         junitVersion = '5.9.0',
     ]

+ 5 - 2
changelog-temp.md

@@ -1,2 +1,5 @@
-
-
+- fix: 修复Service多层代理引发的错误
+- fix: 修复Json类型处理器反序列化泛型丢失原始类型
+- fix: 去除sqlFirst与sqlComment转义(如有需要转义操作,请手动调用转义后传入)
+- feat: 支持组合注解
+- feat: 升级spring-cloud-commons

+ 1 - 1
gradle.properties

@@ -1,4 +1,4 @@
-APP_VERSION=3.5.5
+APP_VERSION=3.5.6-SNAPSHOT
 APP_GROUP=com.baomidou
 signing.keyId=1FD337F9
 signing.password=243194995

+ 3 - 5
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java

@@ -28,11 +28,9 @@ import org.apache.ibatis.binding.MapperMethod;
 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.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;
@@ -77,13 +75,13 @@ public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
         .collect(Collectors.toSet());
 
     private final Configuration configuration;
-    private final MapperBuilderAssistant assistant;
+    private final MybatisMapperBuilderAssistant assistant;
     private final Class<?> type;
 
     public MybatisMapperAnnotationBuilder(Configuration configuration, Class<?> type) {
         super(configuration, type);
         String resource = type.getName().replace(StringPool.DOT, StringPool.SLASH) + ".java (best guess)";
-        this.assistant = new MapperBuilderAssistant(configuration, resource);
+        this.assistant = new MybatisMapperBuilderAssistant(configuration, resource);
         this.configuration = configuration;
         this.type = type;
     }
@@ -167,7 +165,7 @@ public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
                 }
             }
             if (inputStream != null) {
-                XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
+                MybatisXMLMapperBuilder xmlParser = new MybatisXMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
                 xmlParser.parse();
             }
         }

+ 131 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java

@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2011-2023, baomidou (jobob@qq.com).
+ *
+ * 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.
+ */
+package com.baomidou.mybatisplus.core;
+
+import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
+import com.baomidou.mybatisplus.core.toolkit.MybatisUtils;
+import org.apache.ibatis.builder.MapperBuilderAssistant;
+import org.apache.ibatis.mapping.ResultFlag;
+import org.apache.ibatis.mapping.ResultMapping;
+import org.apache.ibatis.reflection.MetaClass;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.TypeHandler;
+import org.apache.ibatis.type.UnknownTypeHandler;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * 重写了原生方法.
+ *
+ * @author nieqiurong
+ * @see MapperBuilderAssistant
+ * @since 3.5.6
+ */
+public class MybatisMapperBuilderAssistant extends MapperBuilderAssistant {
+
+    public MybatisMapperBuilderAssistant(Configuration configuration, String resource) {
+        super(configuration, resource);
+    }
+
+    public ResultMapping buildResultMapping(Class<?> resultType, String property, String column, Class<?> javaType,
+                                            JdbcType jdbcType, String nestedSelect, String nestedResultMap, String notNullColumn, String columnPrefix,
+                                            Class<? extends TypeHandler<?>> typeHandler, List<ResultFlag> flags, String resultSet, String foreignColumn,
+                                            boolean lazy) {
+        Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
+        TypeHandler<?> typeHandlerInstance = null;
+        if (typeHandler != null && typeHandler != UnknownTypeHandler.class) {
+            if (IJsonTypeHandler.class.isAssignableFrom(typeHandler)) {
+                try {
+                    Field field = resultType.getDeclaredField(property);
+                    typeHandlerInstance = MybatisUtils.newJsonTypeHandler(typeHandler, javaTypeClass, field);
+                } catch (NoSuchFieldException e) {
+                    //ignore 降级兼容处理
+                    typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
+                }
+            } else {
+                typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
+            }
+        }
+        List<ResultMapping> composites;
+        if ((nestedSelect == null || nestedSelect.isEmpty()) && (foreignColumn == null || foreignColumn.isEmpty())) {
+            composites = Collections.emptyList();
+        } else {
+            composites = parseCompositeColumnName(column);
+        }
+        return new ResultMapping.Builder(configuration, property, column, javaTypeClass).jdbcType(jdbcType)
+            .nestedQueryId(applyCurrentNamespace(nestedSelect, true))
+            .nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)).resultSet(resultSet)
+            .typeHandler(typeHandlerInstance).flags(flags == null ? new ArrayList<>() : flags).composites(composites)
+            .notNullColumns(parseMultipleColumnNames(notNullColumn)).columnPrefix(columnPrefix).foreignColumn(foreignColumn)
+            .lazy(lazy).build();
+    }
+
+
+    private Set<String> parseMultipleColumnNames(String columnName) {
+        Set<String> columns = new HashSet<>();
+        if (columnName != null) {
+            if (columnName.indexOf(',') > -1) {
+                StringTokenizer parser = new StringTokenizer(columnName, "{}, ", false);
+                while (parser.hasMoreTokens()) {
+                    String column = parser.nextToken();
+                    columns.add(column);
+                }
+            } else {
+                columns.add(columnName);
+            }
+        }
+        return columns;
+    }
+
+
+    private List<ResultMapping> parseCompositeColumnName(String columnName) {
+        List<ResultMapping> composites = new ArrayList<>();
+        if (columnName != null && (columnName.indexOf('=') > -1 || columnName.indexOf(',') > -1)) {
+            StringTokenizer parser = new StringTokenizer(columnName, "{}=, ", false);
+            while (parser.hasMoreTokens()) {
+                String property = parser.nextToken();
+                String column = parser.nextToken();
+                ResultMapping complexResultMapping = new ResultMapping.Builder(configuration, property, column,
+                    configuration.getTypeHandlerRegistry().getUnknownTypeHandler()).build();
+                composites.add(complexResultMapping);
+            }
+        }
+        return composites;
+    }
+
+    private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
+        if (javaType == null && property != null) {
+            try {
+                MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());
+                javaType = metaResultType.getSetterType(property);
+            } catch (Exception e) {
+                // ignore, following null check statement will deal with the situation
+            }
+        }
+        if (javaType == null) {
+            javaType = Object.class;
+        }
+        return javaType;
+    }
+
+}

+ 2 - 3
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java

@@ -18,7 +18,6 @@ package com.baomidou.mybatisplus.core;
 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;
@@ -377,13 +376,13 @@ public class MybatisXMLConfigBuilder extends BaseBuilder {
                     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());
+                            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)) {
-                            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
+                            MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                             mapperParser.parse();
                         }
                     } else if (resource == null && url == null && mapperClass != null) {

+ 451 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java

@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2011-2023, baomidou (jobob@qq.com).
+ *
+ * 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.
+ */
+package com.baomidou.mybatisplus.core;
+
+import org.apache.ibatis.builder.BaseBuilder;
+import org.apache.ibatis.builder.BuilderException;
+import org.apache.ibatis.builder.CacheRefResolver;
+import org.apache.ibatis.builder.IncompleteElementException;
+import org.apache.ibatis.builder.ResultMapResolver;
+import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
+import org.apache.ibatis.builder.xml.XMLStatementBuilder;
+import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.executor.ErrorContext;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.mapping.Discriminator;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.mapping.ParameterMode;
+import org.apache.ibatis.mapping.ResultFlag;
+import org.apache.ibatis.mapping.ResultMap;
+import org.apache.ibatis.mapping.ResultMapping;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.parsing.XPathParser;
+import org.apache.ibatis.reflection.MetaClass;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.TypeHandler;
+
+import java.io.InputStream;
+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;
+
+/**
+ * 重写了{@link org.apache.ibatis.builder.xml.XMLMapperBuilder} 替换了 {@link org.apache.ibatis.builder.MapperBuilderAssistant}
+ *
+ * @author nieqiurong
+ * @since 3.5.6
+ */
+public class MybatisXMLMapperBuilder extends BaseBuilder {
+
+    private final XPathParser parser;
+    private final MybatisMapperBuilderAssistant builderAssistant;
+    private final Map<String, XNode> sqlFragments;
+    private final String resource;
+
+    @Deprecated
+    public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments,
+                                   String namespace) {
+        this(reader, configuration, resource, sqlFragments);
+        this.builderAssistant.setCurrentNamespace(namespace);
+    }
+
+    @Deprecated
+    public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource,
+                                   Map<String, XNode> sqlFragments) {
+        this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration,
+            resource, sqlFragments);
+    }
+
+    public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource,
+                                   Map<String, XNode> sqlFragments, String namespace) {
+        this(inputStream, configuration, resource, sqlFragments);
+        this.builderAssistant.setCurrentNamespace(namespace);
+    }
+
+    public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource,
+                                   Map<String, XNode> sqlFragments) {
+        this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration,
+            resource, sqlFragments);
+    }
+
+    private MybatisXMLMapperBuilder(XPathParser parser, Configuration configuration, String resource,
+                                    Map<String, XNode> sqlFragments) {
+        super(configuration);
+        this.builderAssistant = new MybatisMapperBuilderAssistant(configuration, resource);
+        this.parser = parser;
+        this.sqlFragments = sqlFragments;
+        this.resource = resource;
+    }
+
+    public void parse() {
+        if (!configuration.isResourceLoaded(resource)) {
+            configurationElement(parser.evalNode("/mapper"));
+            configuration.addLoadedResource(resource);
+            bindMapperForNamespace();
+        }
+        parsePendingResultMaps();
+        parsePendingCacheRefs();
+        parsePendingStatements();
+    }
+
+    public XNode getSqlFragment(String refid) {
+        return sqlFragments.get(refid);
+    }
+
+    private void configurationElement(XNode context) {
+        try {
+            String namespace = context.getStringAttribute("namespace");
+            if (namespace == null || namespace.isEmpty()) {
+                throw new BuilderException("Mapper's namespace cannot be empty");
+            }
+            builderAssistant.setCurrentNamespace(namespace);
+            cacheRefElement(context.evalNode("cache-ref"));
+            cacheElement(context.evalNode("cache"));
+            parameterMapElement(context.evalNodes("/mapper/parameterMap"));
+            resultMapElements(context.evalNodes("/mapper/resultMap"));
+            sqlElement(context.evalNodes("/mapper/sql"));
+            buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
+        } catch (Exception e) {
+            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
+        }
+    }
+
+    private void buildStatementFromContext(List<XNode> list) {
+        if (configuration.getDatabaseId() != null) {
+            buildStatementFromContext(list, configuration.getDatabaseId());
+        }
+        buildStatementFromContext(list, null);
+    }
+
+    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
+        for (XNode context : list) {
+            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context,
+                requiredDatabaseId);
+            try {
+                statementParser.parseStatementNode();
+            } catch (IncompleteElementException e) {
+                configuration.addIncompleteStatement(statementParser);
+            }
+        }
+    }
+
+    private void parsePendingResultMaps() {
+        Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
+        synchronized (incompleteResultMaps) {
+            Iterator<ResultMapResolver> 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<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
+        synchronized (incompleteCacheRefs) {
+            Iterator<CacheRefResolver> 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<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
+        synchronized (incompleteStatements) {
+            Iterator<XMLStatementBuilder> 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"));
+            CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant,
+                context.getStringAttribute("namespace"));
+            try {
+                cacheRefResolver.resolveCacheRef();
+            } catch (IncompleteElementException e) {
+                configuration.addIncompleteCacheRef(cacheRefResolver);
+            }
+        }
+    }
+
+    private void cacheElement(XNode context) {
+        if (context != null) {
+            String type = context.getStringAttribute("type", "PERPETUAL");
+            Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
+            String eviction = context.getStringAttribute("eviction", "LRU");
+            Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
+            Long flushInterval = context.getLongAttribute("flushInterval");
+            Integer size = context.getIntAttribute("size");
+            boolean readWrite = !context.getBooleanAttribute("readOnly", false);
+            boolean blocking = context.getBooleanAttribute("blocking", false);
+            Properties props = context.getChildrenAsProperties();
+            builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
+        }
+    }
+
+    private void parameterMapElement(List<XNode> list) {
+        for (XNode parameterMapNode : list) {
+            String id = parameterMapNode.getStringAttribute("id");
+            String type = parameterMapNode.getStringAttribute("type");
+            Class<?> parameterClass = resolveClass(type);
+            List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
+            List<ParameterMapping> parameterMappings = new ArrayList<>();
+            for (XNode parameterNode : parameterNodes) {
+                String property = parameterNode.getStringAttribute("property");
+                String javaType = parameterNode.getStringAttribute("javaType");
+                String jdbcType = parameterNode.getStringAttribute("jdbcType");
+                String resultMap = parameterNode.getStringAttribute("resultMap");
+                String mode = parameterNode.getStringAttribute("mode");
+                String typeHandler = parameterNode.getStringAttribute("typeHandler");
+                Integer numericScale = parameterNode.getIntAttribute("numericScale");
+                ParameterMode modeEnum = resolveParameterMode(mode);
+                Class<?> javaTypeClass = resolveClass(javaType);
+                JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
+                Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
+                ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property,
+                    javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
+                parameterMappings.add(parameterMapping);
+            }
+            builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
+        }
+    }
+
+    private void resultMapElements(List<XNode> list) {
+        for (XNode resultMapNode : list) {
+            try {
+                resultMapElement(resultMapNode);
+            } catch (IncompleteElementException e) {
+                // ignore, it will be retried
+            }
+        }
+    }
+
+    private ResultMap resultMapElement(XNode resultMapNode) {
+        return resultMapElement(resultMapNode, Collections.emptyList(), null);
+    }
+
+    private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings,
+                                       Class<?> enclosingType) {
+        ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
+        String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType",
+            resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType"))));
+        Class<?> typeClass = resolveClass(type);
+        if (typeClass == null) {
+            typeClass = inheritEnclosingType(resultMapNode, enclosingType);
+        }
+        Discriminator discriminator = null;
+        List<ResultMapping> resultMappings = new ArrayList<>(additionalResultMappings);
+        List<XNode> resultChildren = resultMapNode.getChildren();
+        for (XNode resultChild : resultChildren) {
+            if ("constructor".equals(resultChild.getName())) {
+                processConstructorElement(resultChild, typeClass, resultMappings);
+            } else if ("discriminator".equals(resultChild.getName())) {
+                discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
+            } else {
+                List<ResultFlag> flags = new ArrayList<>();
+                if ("id".equals(resultChild.getName())) {
+                    flags.add(ResultFlag.ID);
+                }
+                resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
+            }
+        }
+        String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
+        String extend = resultMapNode.getStringAttribute("extends");
+        Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
+        ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator,
+            resultMappings, autoMapping);
+        try {
+            return resultMapResolver.resolve();
+        } catch (IncompleteElementException e) {
+            configuration.addIncompleteResultMap(resultMapResolver);
+            throw e;
+        }
+    }
+
+    protected Class<?> inheritEnclosingType(XNode resultMapNode, Class<?> enclosingType) {
+        if ("association".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) {
+            String property = resultMapNode.getStringAttribute("property");
+            if (property != null && enclosingType != null) {
+                MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory());
+                return metaResultType.getSetterType(property);
+            }
+        } else if ("case".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) {
+            return enclosingType;
+        }
+        return null;
+    }
+
+    private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) {
+        List<XNode> argChildren = resultChild.getChildren();
+        for (XNode argChild : argChildren) {
+            List<ResultFlag> flags = new ArrayList<>();
+            flags.add(ResultFlag.CONSTRUCTOR);
+            if ("idArg".equals(argChild.getName())) {
+                flags.add(ResultFlag.ID);
+            }
+            resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
+        }
+    }
+
+    private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType,
+                                                      List<ResultMapping> resultMappings) {
+        String column = context.getStringAttribute("column");
+        String javaType = context.getStringAttribute("javaType");
+        String jdbcType = context.getStringAttribute("jdbcType");
+        String typeHandler = context.getStringAttribute("typeHandler");
+        Class<?> javaTypeClass = resolveClass(javaType);
+        Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
+        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
+        Map<String, String> discriminatorMap = new HashMap<>();
+        for (XNode caseChild : context.getChildren()) {
+            String value = caseChild.getStringAttribute("value");
+            String resultMap = caseChild.getStringAttribute("resultMap",
+                processNestedResultMappings(caseChild, resultMappings, resultType));
+            discriminatorMap.put(value, resultMap);
+        }
+        return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass,
+            discriminatorMap);
+    }
+
+    private void sqlElement(List<XNode> list) {
+        if (configuration.getDatabaseId() != null) {
+            sqlElement(list, configuration.getDatabaseId());
+        }
+        sqlElement(list, null);
+    }
+
+    private void sqlElement(List<XNode> list, String requiredDatabaseId) {
+        for (XNode context : list) {
+            String databaseId = context.getStringAttribute("databaseId");
+            String id = context.getStringAttribute("id");
+            id = builderAssistant.applyCurrentNamespace(id, false);
+            if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
+                sqlFragments.put(id, context);
+            }
+        }
+    }
+
+    private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
+        if (requiredDatabaseId != null) {
+            return requiredDatabaseId.equals(databaseId);
+        }
+        if (databaseId != null) {
+            return false;
+        }
+        if (!this.sqlFragments.containsKey(id)) {
+            return true;
+        }
+        // skip this fragment if there is a previous one with a not null databaseId
+        XNode context = this.sqlFragments.get(id);
+        return context.getStringAttribute("databaseId") == null;
+    }
+
+    private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) {
+        String property;
+        if (flags.contains(ResultFlag.CONSTRUCTOR)) {
+            property = context.getStringAttribute("name");
+        } else {
+            property = context.getStringAttribute("property");
+        }
+        String column = context.getStringAttribute("column");
+        String javaType = context.getStringAttribute("javaType");
+        String jdbcType = context.getStringAttribute("jdbcType");
+        String nestedSelect = context.getStringAttribute("select");
+        String nestedResultMap = context.getStringAttribute("resultMap",
+            () -> processNestedResultMappings(context, Collections.emptyList(), resultType));
+        String notNullColumn = context.getStringAttribute("notNullColumn");
+        String columnPrefix = context.getStringAttribute("columnPrefix");
+        String typeHandler = context.getStringAttribute("typeHandler");
+        String resultSet = context.getStringAttribute("resultSet");
+        String foreignColumn = context.getStringAttribute("foreignColumn");
+        boolean lazy = "lazy"
+            .equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
+        Class<?> javaTypeClass = resolveClass(javaType);
+        Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
+        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
+        return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect,
+            nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
+    }
+
+    private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings,
+                                               Class<?> enclosingType) {
+        if (Arrays.asList("association", "collection", "case").contains(context.getName())
+            && context.getStringAttribute("select") == null) {
+            validateCollection(context, enclosingType);
+            ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
+            return resultMap.getId();
+        }
+        return null;
+    }
+
+    protected void validateCollection(XNode context, Class<?> enclosingType) {
+        if ("collection".equals(context.getName()) && context.getStringAttribute("resultMap") == null
+            && context.getStringAttribute("javaType") == null) {
+            MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory());
+            String property = context.getStringAttribute("property");
+            if (!metaResultType.hasSetter(property)) {
+                throw new BuilderException(
+                    "Ambiguous collection type for property '" + property + "'. You must specify 'javaType' or 'resultMap'.");
+            }
+        }
+    }
+
+    private void bindMapperForNamespace() {
+        String namespace = builderAssistant.getCurrentNamespace();
+        if (namespace != null) {
+            Class<?> boundType = null;
+            try {
+                boundType = Resources.classForName(namespace);
+            } catch (ClassNotFoundException e) {
+                // ignore, bound type is not required
+            }
+            if (boundType != null && !configuration.hasMapper(boundType)) {
+                // Spring may not know the real resource name so we set a flag
+                // to prevent loading again this resource from the mapper interface
+                // look at MapperAnnotationBuilder#loadXmlResource
+                configuration.addLoadedResource("namespace:" + namespace);
+                configuration.addMapper(boundType);
+            }
+        }
+    }
+}

+ 2 - 2
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractWrapper.java

@@ -622,7 +622,7 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
     @Override
     public String getSqlComment() {
         if (StringUtils.isNotBlank(sqlComment.getStringValue())) {
-            return "/*" + StringEscape.escapeRawString(sqlComment.getStringValue()) + "*/";
+            return "/*" + sqlComment.getStringValue() + "*/";
         }
         return null;
     }
@@ -630,7 +630,7 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
     @Override
     public String getSqlFirst() {
         if (StringUtils.isNotBlank(sqlFirst.getStringValue())) {
-            return StringEscape.escapeRawString(sqlFirst.getStringValue());
+            return sqlFirst.getStringValue();
         }
         return null;
     }

+ 14 - 2
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Join.java

@@ -97,8 +97,11 @@ public interface Join<Children> extends Serializable {
 
     /**
      * sql 注释(会拼接在 sql 的最后面)
+     * <p>
+     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
+     * </p>
      *
-     * @param comment   sql注释
+     * @param comment sql注释
      * @return children
      */
     default Children comment(String comment) {
@@ -107,6 +110,9 @@ public interface Join<Children> extends Serializable {
 
     /**
      * sql 注释(会拼接在 sql 的最后面)
+     * <p>
+     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
+     * </p>
      *
      * @param condition 执行条件
      * @param comment   sql注释
@@ -116,8 +122,11 @@ public interface Join<Children> extends Serializable {
 
     /**
      * sql 起始句(会拼接在SQL语句的起始处)
+     * <p>
+     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
+     * </p>
      *
-     * @param firstSql  起始语句
+     * @param firstSql 起始语句
      * @return children
      */
     default Children first(String firstSql) {
@@ -126,6 +135,9 @@ public interface Join<Children> extends Serializable {
 
     /**
      * sql 起始句(会拼接在SQL语句的起始处)
+     * <p>
+     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
+     * </p>
      *
      * @param condition 执行条件
      * @param firstSql  起始语句

+ 8 - 6
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/AnnotationHandler.java

@@ -15,6 +15,8 @@
  */
 package com.baomidou.mybatisplus.core.handlers;
 
+import com.baomidou.mybatisplus.core.toolkit.AnnotationUtils;
+
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -34,7 +36,7 @@ public interface AnnotationHandler {
      * @return 注解
      */
     default <T extends Annotation> T getAnnotation(Class<?> beanClass, Class<T> annotationClass) {
-        return beanClass.getAnnotation(annotationClass);
+        return AnnotationUtils.findFirstAnnotation(annotationClass, beanClass);
     }
 
     /**
@@ -46,7 +48,7 @@ public interface AnnotationHandler {
      * @return 是否包含该注解
      */
     default <T extends Annotation> boolean isAnnotationPresent(Class<?> beanClass, Class<T> annotationClass) {
-        return beanClass.isAnnotationPresent(annotationClass);
+        return AnnotationUtils.findFirstAnnotation(annotationClass, beanClass) != null;
     }
 
     /**
@@ -58,7 +60,7 @@ public interface AnnotationHandler {
      * @return 注解
      */
     default <T extends Annotation> T getAnnotation(Field field, Class<T> annotationClass) {
-        return field.getAnnotation(annotationClass);
+        return AnnotationUtils.findFirstAnnotation(annotationClass, field);
     }
 
     /**
@@ -70,7 +72,7 @@ public interface AnnotationHandler {
      * @return 是否包含该注解
      */
     default <T extends Annotation> boolean isAnnotationPresent(Field field, Class<T> annotationClass) {
-        return field.isAnnotationPresent(annotationClass);
+        return AnnotationUtils.findFirstAnnotation(annotationClass, field) != null;
     }
 
     /**
@@ -82,7 +84,7 @@ public interface AnnotationHandler {
      * @return 注解
      */
     default <T extends Annotation> T getAnnotation(Method method, Class<T> annotationClass) {
-        return method.getAnnotation(annotationClass);
+        return AnnotationUtils.findFirstAnnotation(annotationClass, method);
     }
 
     /**
@@ -94,6 +96,6 @@ public interface AnnotationHandler {
      * @return 是否包含该注解
      */
     default <T extends Annotation> boolean isAnnotationPresent(Method method, Class<T> annotationClass) {
-        return method.isAnnotationPresent(annotationClass);
+        return AnnotationUtils.findFirstAnnotation(annotationClass, method) != null;
     }
 }

+ 49 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/IJsonTypeHandler.java

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011-2023, baomidou (jobob@qq.com).
+ *
+ * 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.
+ */
+package com.baomidou.mybatisplus.core.handlers;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+
+/**
+ * Json类型处理器接口(实现类确保为多例状态).
+ * <p>
+ * 注意:查询返回时需要使用resultMap
+ * </p>
+ *
+ * @author nieqiurong 2024年3月4日
+ * @see TableName#autoResultMap() 自动构建
+ * @see TableName#resultMap() 手动指定
+ * @since 3.5.6
+ */
+public interface IJsonTypeHandler<T> {
+
+    /**
+     * 反序列化json
+     *
+     * @param json json字符串
+     * @return T
+     */
+    T parse(String json);
+
+    /**
+     * 序列化json
+     *
+     * @param obj 对象信息
+     * @return json字符串
+     */
+    String toJson(Object obj);
+
+}

+ 14 - 7
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/TableFieldInfo.java

@@ -23,7 +23,9 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.Version;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.core.toolkit.MybatisUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
 import lombok.AccessLevel;
@@ -552,16 +554,21 @@ public class TableFieldInfo implements Constants {
      * @return ResultMapping
      */
     ResultMapping getResultMapping(final Configuration configuration) {
-        ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property,
-            StringUtils.getTargetColumn(column), propertyType);
+        ResultMapping.Builder builder = new ResultMapping.Builder(configuration, this.property,
+            StringUtils.getTargetColumn(this.column), this.propertyType);
         TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();
-        if (jdbcType != null && jdbcType != JdbcType.UNDEFINED) {
-            builder.jdbcType(jdbcType);
+        if (this.jdbcType != null && this.jdbcType != JdbcType.UNDEFINED) {
+            builder.jdbcType(this.jdbcType);
         }
-        if (typeHandler != null && typeHandler != UnknownTypeHandler.class) {
+        if (this.typeHandler != null && this.typeHandler != UnknownTypeHandler.class) {
             TypeHandler<?> typeHandler = registry.getMappingTypeHandler(this.typeHandler);
-            if (typeHandler == null) {
-                typeHandler = registry.getInstance(propertyType, this.typeHandler);
+            if (IJsonTypeHandler.class.isAssignableFrom(this.typeHandler)) {
+                // 保证每次实例化
+                typeHandler = MybatisUtils.newJsonTypeHandler(this.typeHandler, this.propertyType, this.field);
+            } else {
+                if (typeHandler == null) {
+                    typeHandler = registry.getInstance(this.propertyType, this.typeHandler);
+                }
             }
             builder.typeHandler(typeHandler);
         }

+ 74 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/AnnotationUtils.java

@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011-2023, baomidou (jobob@qq.com).
+ *
+ * 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.
+ */
+package com.baomidou.mybatisplus.core.toolkit;
+
+import lombok.experimental.UtilityClass;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author nieqiurong
+ * @since 3.5.6
+ */
+@UtilityClass
+public class AnnotationUtils {
+
+    public <T extends Annotation> T findFirstAnnotation(Class<T> annotationClazz, Field field) {
+        return getAnnotation(annotationClazz, new HashSet<>(), field.getDeclaredAnnotations());
+    }
+
+    public <T extends Annotation> T findFirstAnnotation(Class<T> annotationClazz, Class<?> clz) {
+        Set<Class<? extends Annotation>> hashSet = new HashSet<>();
+        T annotation = getAnnotation(annotationClazz, hashSet, clz.getDeclaredAnnotations());
+        if (annotation != null) {
+            return annotation;
+        }
+        Class<?> currentClass = clz.getSuperclass();
+        while (currentClass != null) {
+            annotation = getAnnotation(annotationClazz, hashSet, currentClass.getDeclaredAnnotations());
+            if (annotation != null) {
+                return annotation;
+            }
+            currentClass = currentClass.getSuperclass();
+        }
+        return null;
+    }
+
+    public <T extends Annotation> T findFirstAnnotation(Class<T> annotationClazz, Method method) {
+        return getAnnotation(annotationClazz, new HashSet<>(), method.getDeclaredAnnotations());
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends Annotation> T getAnnotation(Class<T> annotationClazz, Set<Class<? extends Annotation>> annotationSet, Annotation... annotations) {
+        for (Annotation annotation : annotations) {
+            if (annotationSet.add(annotation.annotationType())) {
+                if (annotationClazz.isAssignableFrom(annotation.annotationType())) {
+                    return (T) annotation;
+                }
+                annotation = getAnnotation(annotationClazz, annotationSet, annotation.annotationType().getDeclaredAnnotations());
+                if (annotation != null) {
+                    return (T) annotation;
+                }
+            }
+        }
+        return null;
+    }
+
+}

+ 52 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/MybatisUtils.java

@@ -0,0 +1,52 @@
+package com.baomidou.mybatisplus.core.toolkit;
+
+import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.type.TypeException;
+import org.apache.ibatis.type.TypeHandler;
+
+import java.lang.reflect.Field;
+
+/**
+ * @author nieqiurong
+ * @since 3.5.6
+ */
+@Slf4j
+@UtilityClass
+public class MybatisUtils {
+
+    /**
+     * 实例化Json类型处理器
+     * <p>
+     * 1.子类需要包含构造(Class,Field)
+     * 2.如果无上述构造或者无属性字段,则使用默认构造(Class)进行实例化
+     * </p>
+     *
+     * @param typeHandler   类型处理器 {@link IJsonTypeHandler}
+     * @param javaTypeClass java类型信息
+     * @param field         属性字段
+     * @return 实例化类型处理器
+     */
+    public static TypeHandler<?> newJsonTypeHandler(Class<? extends TypeHandler<?>> typeHandler, Class<?> javaTypeClass, Field field) {
+        TypeHandler<?> result = null;
+        if (IJsonTypeHandler.class.isAssignableFrom(typeHandler)) {
+            if (field != null) {
+                try {
+                    result = typeHandler.getConstructor(Class.class, Field.class).newInstance(javaTypeClass, field);
+                } catch (ReflectiveOperationException e) {
+                    // ignore
+                }
+            }
+            if (result == null) {
+                try {
+                    result = typeHandler.getConstructor(Class.class).newInstance(javaTypeClass);
+                } catch (ReflectiveOperationException ex) {
+                    throw new TypeException("Failed invoking constructor for handler " + typeHandler, ex);
+                }
+            }
+        }
+        return result;
+    }
+
+}

+ 70 - 0
mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/metadata/TableInfoHelperTest.java

@@ -17,6 +17,12 @@ import org.apache.ibatis.mapping.ResultMap;
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -71,6 +77,70 @@ class TableInfoHelperTest {
         private String name;
     }
 
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+    @TableField(exist = false)
+    @interface NotExistsField {
+
+    }
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+    @TableId
+    @interface MyId {
+
+    }
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
+    @TableName(schema = "test")
+    @interface MyTable {
+
+    }
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+    @TableLogic(value = "false", delval = "true")
+    @interface MyTableLogic {
+
+    }
+
+
+    @Data
+    @MyTable
+    private static class CustomAnnotation {
+
+        @MyId
+        private Long id;
+
+        private String name;
+
+        @NotExistsField
+        private String test;
+
+        @MyTableLogic
+        private Boolean del;
+    }
+
+    @Test
+    void testCustomAnnotation() {
+        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
+        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(mybatisConfiguration, ""), CustomAnnotation.class);
+        List<Field> fieldList = TableInfoHelper.getAllFields(CustomAnnotation.class);
+        Assertions.assertThat(fieldList.size()).isEqualTo(3);
+        Assertions.assertThat(TableInfoHelper.isExistTableId(CustomAnnotation.class, Arrays.asList(CustomAnnotation.class.getDeclaredFields()))).isTrue();
+        Assertions.assertThat(TableInfoHelper.isExistTableLogic(CustomAnnotation.class, Arrays.asList(CustomAnnotation.class.getDeclaredFields()))).isTrue();
+        TableFieldInfo logicDeleteFieldInfo = TableInfoHelper.getTableInfo(CustomAnnotation.class).getLogicDeleteFieldInfo();
+        Assertions.assertThat(logicDeleteFieldInfo).isNotNull();
+        Assertions.assertThat(logicDeleteFieldInfo.getLogicDeleteValue()).isNotNull().isEqualTo("true");
+        Assertions.assertThat(logicDeleteFieldInfo.getLogicNotDeleteValue()).isNotNull().isEqualTo("false");
+        Assertions.assertThat(tableInfo.getTableName()).isEqualTo("test.custom_annotation");
+    }
+
 
     @Test
     void testIsExistTableId() {

+ 152 - 0
mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/AnnotationUtilsTest.java

@@ -0,0 +1,152 @@
+package com.baomidou.mybatisplus.test.toolkit;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.toolkit.AnnotationUtils;
+import lombok.Data;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author nieqiurong
+ */
+public class AnnotationUtilsTest {
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+    @TableField(exist = false)
+    @interface TableField2 {
+
+    }
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+    @TableField(exist = true)
+    @interface TableField3 {
+
+    }
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+    @TableField2
+    @interface TableField4 {
+
+    }
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
+    @TableName(schema = "test")
+    @interface MyTable {
+
+    }
+
+
+    @Data
+    @MyTable
+    public static class Demo {
+
+        private String id;
+
+        @TableField
+        private String name;
+
+        @TableField2
+        private String name1;
+
+        @TableField3
+        private String name2;
+
+        @TableField4
+        private String name3;
+
+        @TableField4
+        @TableField3
+        private String name4;
+
+        @TableField3
+        @TableField4
+        private String name5;
+
+        @TableField(fill = FieldFill.UPDATE)
+        @TableLogic(delval = "true", value = "false")
+        private Boolean deleted;
+
+    }
+
+    @Test
+    void test() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("id"));
+        Assertions.assertNull(tableField);
+    }
+
+    @Test
+    void test1() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("name"));
+        Assertions.assertNotNull(tableField);
+        Assertions.assertTrue(tableField.exist());
+    }
+
+    @Test
+    void test2() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("name1"));
+        Assertions.assertNotNull(tableField);
+        Assertions.assertFalse(tableField.exist());
+    }
+
+    @Test
+    void test3() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("name2"));
+        Assertions.assertNotNull(tableField);
+        Assertions.assertTrue(tableField.exist());
+    }
+
+    @Test
+    void test4() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("name3"));
+        Assertions.assertNotNull(tableField);
+        Assertions.assertFalse(tableField.exist());
+    }
+
+    @Test
+    void test5() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("name4"));
+        Assertions.assertNotNull(tableField);
+        Assertions.assertFalse(tableField.exist());
+    }
+
+    @Test
+    void test6() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("name5"));
+        Assertions.assertNotNull(tableField);
+        Assertions.assertTrue(tableField.exist());
+    }
+
+    @Test
+    void test7() throws Exception {
+        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField("deleted"));
+        Assertions.assertNotNull(tableField);
+        Assertions.assertTrue(tableField.exist());
+        TableLogic tableLogic = AnnotationUtils.findFirstAnnotation(TableLogic.class, Demo.class.getDeclaredField("deleted"));
+        Assertions.assertNotNull(tableLogic);
+    }
+
+    @Test
+    void test8() {
+        TableName tableName = AnnotationUtils.findFirstAnnotation(TableName.class, Demo.class);
+        Assertions.assertNotNull(tableName);
+        Assertions.assertEquals(tableName.schema(),"test");
+    }
+
+}

+ 43 - 3
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/AbstractJsonTypeHandler.java

@@ -15,10 +15,16 @@
  */
 package com.baomidou.mybatisplus.extension.handlers;
 
+import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
+import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import org.apache.ibatis.type.BaseTypeHandler;
 import org.apache.ibatis.type.JdbcType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -28,7 +34,40 @@ import java.sql.SQLException;
  * @author miemie
  * @since 2019-11-28
  */
-public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> {
+public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> implements IJsonTypeHandler<T> {
+
+    protected final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    protected final Class<?> type;
+
+    /**
+     * @since 3.5.6
+     */
+    protected Type genericType;
+
+    /**
+     * 默认初始化
+     *
+     * @param type 类型
+     */
+    public AbstractJsonTypeHandler(Class<?> type) {
+        this.type = type;
+        if (log.isTraceEnabled()) {
+            log.trace(this.getClass().getSimpleName() + "(" + type + ")");
+        }
+        Assert.notNull(type, "Type argument cannot be null");
+    }
+
+    /**
+     * 通过字段初始化
+     *
+     * @param type  类型
+     * @param field 字段
+     */
+    public AbstractJsonTypeHandler(Class<?> type, Field field) {
+        this(type);
+        this.genericType = field.getGenericType();
+    }
 
     @Override
     public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
@@ -53,7 +92,8 @@ public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> {
         return StringUtils.isBlank(json) ? null : parse(json);
     }
 
-    protected abstract T parse(String json);
+    public Type getFieldType() {
+        return this.genericType != null ? this.genericType : this.type;
+    }
 
-    protected abstract String toJson(T obj);
 }

+ 9 - 13
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/Fastjson2TypeHandler.java

@@ -17,41 +17,37 @@ package com.baomidou.mybatisplus.extension.handlers;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONWriter;
-import com.baomidou.mybatisplus.core.toolkit.Assert;
-import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.type.JdbcType;
 import org.apache.ibatis.type.MappedJdbcTypes;
 import org.apache.ibatis.type.MappedTypes;
 
+import java.lang.reflect.Field;
+
 /**
  * Fastjson2 实现 JSON 字段类型处理器
  *
  * @author nieqiurong
  * @since 3.5.5
  */
-@Slf4j
 @MappedTypes({Object.class})
 @MappedJdbcTypes(JdbcType.VARCHAR)
 public class Fastjson2TypeHandler extends AbstractJsonTypeHandler<Object> {
 
-    private final Class<?> type;
-
     public Fastjson2TypeHandler(Class<?> type) {
-        if (log.isTraceEnabled()) {
-            log.trace("Fastjson2TypeHandler(" + type + ")");
-        }
-        Assert.notNull(type, "Type argument cannot be null");
-        this.type = type;
+        super(type);
     }
 
+    public Fastjson2TypeHandler(Class<?> type, Field field) {
+        super(type, field);
+    }
 
     @Override
-    protected Object parse(String json) {
-        return JSON.parseObject(json, this.type);
+    public Object parse(String json) {
+        return JSON.parseObject(json, this.getFieldType());
     }
 
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         return JSON.toJSONString(obj, JSONWriter.Feature.WriteMapNullValue,
             JSONWriter.Feature.WriteNullListAsEmpty, JSONWriter.Feature.WriteNullStringAsEmpty);
     }

+ 11 - 9
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/FastjsonTypeHandler.java

@@ -17,40 +17,42 @@ package com.baomidou.mybatisplus.extension.handlers;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.serializer.SerializerFeature;
-import com.baomidou.mybatisplus.core.toolkit.Assert;
-import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.type.JdbcType;
 import org.apache.ibatis.type.MappedJdbcTypes;
 import org.apache.ibatis.type.MappedTypes;
 
+import java.lang.reflect.Field;
+
 /**
  * Fastjson 实现 JSON 字段类型处理器
  *
  * @author hubin
  * @since 2019-08-25
  */
-@Slf4j
 @MappedTypes({Object.class})
 @MappedJdbcTypes(JdbcType.VARCHAR)
 public class FastjsonTypeHandler extends AbstractJsonTypeHandler<Object> {
-    private final Class<?> type;
 
     public FastjsonTypeHandler(Class<?> type) {
+        super(type);
         if (log.isTraceEnabled()) {
             log.trace("FastjsonTypeHandler(" + type + ")");
         }
-        Assert.notNull(type, "Type argument cannot be null");
-        this.type = type;
+    }
+
+    public FastjsonTypeHandler(Class<?> type, Field field) {
+        super(type, field);
     }
 
     @Override
-    protected Object parse(String json) {
-        return JSON.parseObject(json, type);
+    public Object parse(String json) {
+        return JSON.parseObject(json, this.getFieldType());
     }
 
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue,
             SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty);
     }
+
 }

+ 12 - 11
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/GsonTypeHandler.java

@@ -17,39 +17,40 @@ package com.baomidou.mybatisplus.extension.handlers;
 
 import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.google.gson.Gson;
-import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.type.JdbcType;
 import org.apache.ibatis.type.MappedJdbcTypes;
 import org.apache.ibatis.type.MappedTypes;
 
+import java.lang.reflect.Field;
+
 /**
  * Gson 实现 JSON 字段类型处理器
  *
  * @author hubin
  * @since 2019-08-25
  */
-@Slf4j
 @MappedTypes({Object.class})
 @MappedJdbcTypes(JdbcType.VARCHAR)
 public class GsonTypeHandler extends AbstractJsonTypeHandler<Object> {
+
     private static Gson GSON;
-    private final Class<?> type;
 
     public GsonTypeHandler(Class<?> type) {
-        if (log.isTraceEnabled()) {
-            log.trace("GsonTypeHandler(" + type + ")");
-        }
-        Assert.notNull(type, "Type argument cannot be null");
-        this.type = type;
+        super(type);
     }
 
+    public GsonTypeHandler(Class<?> type, Field field) {
+        super(type, field);
+    }
+
+
     @Override
-    protected Object parse(String json) {
-        return getGson().fromJson(json, type);
+    public Object parse(String json) {
+        return getGson().fromJson(json, this.getFieldType());
     }
 
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         return getGson().toJson(obj);
     }
 

+ 18 - 11
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/JacksonTypeHandler.java

@@ -17,13 +17,15 @@ package com.baomidou.mybatisplus.extension.handlers;
 
 import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.type.JdbcType;
 import org.apache.ibatis.type.MappedJdbcTypes;
 import org.apache.ibatis.type.MappedTypes;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
 
 /**
  * Jackson 实现 JSON 字段类型处理器
@@ -31,32 +33,37 @@ import java.io.IOException;
  * @author hubin
  * @since 2019-08-25
  */
-@Slf4j
 @MappedTypes({Object.class})
 @MappedJdbcTypes(JdbcType.VARCHAR)
 public class JacksonTypeHandler extends AbstractJsonTypeHandler<Object> {
+
     private static ObjectMapper OBJECT_MAPPER;
-    private final Class<?> type;
 
     public JacksonTypeHandler(Class<?> type) {
-        if (log.isTraceEnabled()) {
-            log.trace("JacksonTypeHandler(" + type + ")");
-        }
-        Assert.notNull(type, "Type argument cannot be null");
-        this.type = type;
+        super(type);
     }
 
+    public JacksonTypeHandler(Class<?> type, Field field) {
+        super(type, field);
+    }
+
+
     @Override
-    protected Object parse(String json) {
+    public Object parse(String json) {
         try {
-            return getObjectMapper().readValue(json, type);
+            return getObjectMapper().readValue(json, new TypeReference<Object>() {
+                @Override
+                public Type getType() {
+                    return getFieldType();
+                }
+            });
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
     }
 
     @Override
-    protected String toJson(Object obj) {
+    public String toJson(Object obj) {
         try {
             return getObjectMapper().writeValueAsString(obj);
         } catch (JsonProcessingException e) {

+ 3 - 3
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/service/impl/ServiceImpl.java

@@ -92,11 +92,11 @@ public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
                     Object target = this.baseMapper;
                     // 这个检查目前看着来说基本上可以不用判断Aop是不是存在了.
                     if (com.baomidou.mybatisplus.extension.toolkit.AopUtils.isLoadSpringAop()) {
-                        if (AopUtils.isAopProxy(this.baseMapper)) {
-                            target = AopProxyUtils.getSingletonTarget(this.baseMapper);
+                        while (AopUtils.isAopProxy(target)) {
+                            target = AopProxyUtils.getSingletonTarget(target);
                         }
                     }
-                    if (target != null) {
+                    if (target instanceof MybatisMapperProxy) {
                         MybatisMapperProxy mybatisMapperProxy = (MybatisMapperProxy) Proxy.getInvocationHandler(target);
                         SqlSessionTemplate sqlSessionTemplate = (SqlSessionTemplate) mybatisMapperProxy.getSqlSession();
                         this.sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();

+ 2 - 2
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java

@@ -19,11 +19,11 @@ import com.baomidou.mybatisplus.core.MybatisConfiguration;
 import com.baomidou.mybatisplus.core.MybatisPlusVersion;
 import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
 import com.baomidou.mybatisplus.core.MybatisXMLConfigBuilder;
+import com.baomidou.mybatisplus.core.MybatisXMLMapperBuilder;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
 import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
 import lombok.Setter;
-import org.apache.ibatis.builder.xml.XMLMapperBuilder;
 import org.apache.ibatis.cache.Cache;
 import org.apache.ibatis.executor.ErrorContext;
 import org.apache.ibatis.io.Resources;
@@ -557,7 +557,7 @@ public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFacto
                         continue;
                     }
                     try {
-                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
+                        MybatisXMLMapperBuilder xmlMapperBuilder = new MybatisXMLMapperBuilder(mapperLocation.getInputStream(),
                             targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                         xmlMapperBuilder.parse();
                     } catch (Exception e) {

+ 2 - 2
mybatis-plus-extension/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/BaseDbTest.kt

@@ -2,13 +2,13 @@ package com.baomidou.mybatisplus.test.kotlin
 
 import com.baomidou.mybatisplus.core.MybatisConfiguration
 import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder
+import com.baomidou.mybatisplus.core.MybatisXMLMapperBuilder
 import com.baomidou.mybatisplus.core.config.GlobalConfig
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils
 import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils
 import com.baomidou.mybatisplus.core.toolkit.StringUtils
 import com.baomidou.mybatisplus.extension.toolkit.SqlRunner
-import org.apache.ibatis.builder.xml.XMLMapperBuilder
 import org.apache.ibatis.io.Resources
 import org.apache.ibatis.logging.slf4j.Slf4jImpl
 import org.apache.ibatis.mapping.Environment
@@ -65,7 +65,7 @@ abstract class BaseDbTest<T> : TypeReference<T>() {
         if (StringUtils.isNotBlank(mapperXml)) {
             try {
                 val inputStream = Resources.getResourceAsStream(mapperXml)
-                val xmlMapperBuilder = XMLMapperBuilder(
+                val xmlMapperBuilder = MybatisXMLMapperBuilder(
                     inputStream,
                     configuration, mapperXml, configuration.sqlFragments
                 )

+ 2 - 2
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/BaseDbTest.java

@@ -2,12 +2,12 @@ package com.baomidou.mybatisplus.test;
 
 import com.baomidou.mybatisplus.core.MybatisConfiguration;
 import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
+import com.baomidou.mybatisplus.core.MybatisXMLMapperBuilder;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
-import org.apache.ibatis.builder.xml.XMLMapperBuilder;
 import org.apache.ibatis.io.Resources;
 import org.apache.ibatis.logging.slf4j.Slf4jImpl;
 import org.apache.ibatis.mapping.Environment;
@@ -73,7 +73,7 @@ public abstract class BaseDbTest<T> extends TypeReference<T> {
         if (StringUtils.isNotBlank(mapperXml)) {
             try {
                 InputStream inputStream = Resources.getResourceAsStream(mapperXml);
-                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream,
+                MybatisXMLMapperBuilder xmlMapperBuilder = new MybatisXMLMapperBuilder(inputStream,
                     configuration, mapperXml, configuration.getSqlFragments());
                 xmlMapperBuilder.parse();
             } catch (IOException e) {

+ 1 - 1
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/MultiAopTest.java

@@ -17,7 +17,7 @@ import java.util.List;
  * @author nieqiurong
  */
 @ExtendWith(SpringExtension.class)
-@ContextConfiguration(classes = {AppConfig.class, AopConfig2.class})
+@ContextConfiguration(classes = {AppConfig.class, AopConfig1.class, AopConfig2.class})
 public class MultiAopTest {
 
     @Autowired

+ 67 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2Entity.java

@@ -0,0 +1,67 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName(value = "t_fastjson2_entity", autoResultMap = true)
+public class FastJson2Entity {
+
+    @TableId(type = IdType.INPUT)
+    private String id;
+
+    private String name;
+
+    @TableField(typeHandler = Fastjson2TypeHandler.class)
+    private Card card;
+
+    @TableField(typeHandler = Fastjson2TypeHandler.class)
+    private List<Attr> attr;
+
+    @TableField(typeHandler = Fastjson2TypeHandler.class)
+    private Map<String, Attr> attr2;
+
+    @TableField(typeHandler = Fastjson2TypeHandler.class)
+    private Map<String, String> attr3;
+
+    @TableField(typeHandler = Fastjson2TypeHandler.class)
+    private Map<String, Object> attr4;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Attr {
+
+        private String name;
+
+        private String value;
+
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Card {
+
+        private String id;
+
+        private String value;
+
+    }
+
+
+}

+ 10 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2EntityMapper.java

@@ -0,0 +1,10 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public interface FastJson2EntityMapper extends BaseMapper<FastJson2Entity> {
+
+}

+ 67 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntity.java

@@ -0,0 +1,67 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName(value = "t_fastjson_entity", autoResultMap = true)
+public class FastJsonEntity {
+
+    @TableId(type = IdType.INPUT)
+    private String id;
+
+    private String name;
+
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private Card card;
+
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private List<Attr> attr;
+
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private Map<String, Attr> attr2;
+
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private Map<String, String> attr3;
+
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private Map<String, Object> attr4;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Attr {
+
+        private String name;
+
+        private String value;
+
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Card {
+
+        private String id;
+
+        private String value;
+
+    }
+
+
+}

+ 10 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntityMapper.java

@@ -0,0 +1,10 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public interface FastJsonEntityMapper extends BaseMapper<FastJsonEntity> {
+
+}

+ 56 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/Fastjson2Test.java

@@ -0,0 +1,56 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.test.BaseDbTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public class Fastjson2Test extends BaseDbTest<FastJson2EntityMapper> {
+
+    @Override
+    protected List<String> tableSql() {
+        return Arrays.asList("drop table if exists t_fastjson2_entity", "CREATE TABLE IF NOT EXISTS t_fastjson2_entity (" +
+            "id VARCHAR(32) NOT NULL," +
+            "name VARCHAR(30) NULL DEFAULT NULL," +
+            "card VARCHAR(255) NULL DEFAULT NULL," +
+            "attr VARCHAR(255) NULL DEFAULT NULL," +
+            "attr2 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr3 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr4 VARCHAR(255) NULL DEFAULT NULL," +
+            "PRIMARY KEY (id))");
+    }
+
+    @Test
+    void test() {
+        doTest(mapper -> {
+            var entity = new FastJson2Entity("123", "秋秋", new FastJson2Entity.Card("1", "1111"),
+                Arrays.asList(new FastJson2Entity.Attr("age", "18"), new FastJson2Entity.Attr("sex", "女")),
+                new HashMap<>(Map.of("test", new FastJson2Entity.Attr("小红", "1"))),
+                new HashMap<>(Map.of("name", "1", "test2", "2")),
+                new HashMap<>(Map.of("test1", "1", "test2", 2))
+            );
+            mapper.insert(entity);
+            FastJson2Entity jackJsonEntity = mapper.selectById(entity.getId());
+            Assertions.assertEquals("秋秋", jackJsonEntity.getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());
+            Assertions.assertNotNull(jackJsonEntity.getCard());
+            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());
+            Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());
+            Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name"));
+            Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2"));
+            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr4().get("test2"));
+        });
+
+    }
+
+
+}

+ 56 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastjsonTest.java

@@ -0,0 +1,56 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.test.BaseDbTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public class FastjsonTest extends BaseDbTest<FastJsonEntityMapper> {
+
+    @Override
+    protected List<String> tableSql() {
+        return Arrays.asList("drop table if exists t_fastjson_entity", "CREATE TABLE IF NOT EXISTS t_fastjson_entity (" +
+            "id VARCHAR(32) NOT NULL," +
+            "name VARCHAR(30) NULL DEFAULT NULL," +
+            "card VARCHAR(255) NULL DEFAULT NULL," +
+            "attr VARCHAR(255) NULL DEFAULT NULL," +
+            "attr2 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr3 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr4 VARCHAR(255) NULL DEFAULT NULL," +
+            "PRIMARY KEY (id))");
+    }
+
+    @Test
+    void test() {
+        doTest(mapper -> {
+            var entity = new FastJsonEntity("123", "秋秋", new FastJsonEntity.Card("1", "1111"),
+                Arrays.asList(new FastJsonEntity.Attr("age", "18"), new FastJsonEntity.Attr("sex", "女")),
+                new HashMap<>(Map.of("test", new FastJsonEntity.Attr("小红", "1"))),
+                new HashMap<>(Map.of("name", "1", "test2", "2")),
+                new HashMap<>(Map.of("test1", "1", "test2", 2))
+            );
+            mapper.insert(entity);
+            FastJsonEntity jackJsonEntity = mapper.selectById(entity.getId());
+            Assertions.assertEquals("秋秋", jackJsonEntity.getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());
+            Assertions.assertNotNull(jackJsonEntity.getCard());
+            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());
+            Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());
+            Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name"));
+            Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2"));
+            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr4().get("test2"));
+        });
+
+    }
+
+
+}

+ 67 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntity.java

@@ -0,0 +1,67 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName(value = "t_gson_entity", autoResultMap = true)
+public class GsonEntity {
+
+    @TableId(type = IdType.INPUT)
+    private String id;
+
+    private String name;
+
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private Card card;
+
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private List<Attr> attr;
+
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private Map<String, Attr> attr2;
+
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private Map<String, String> attr3;
+
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private Map<String, Object> attr4;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Attr {
+
+        private String name;
+
+        private String value;
+
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Card {
+
+        private String id;
+
+        private String value;
+
+    }
+
+
+}

+ 10 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntityMapper.java

@@ -0,0 +1,10 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public interface GsonEntityMapper extends BaseMapper<GsonEntity> {
+
+}

+ 57 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonTest.java

@@ -0,0 +1,57 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.test.BaseDbTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public class GsonTest extends BaseDbTest<GsonEntityMapper> {
+
+    @Override
+    protected List<String> tableSql() {
+        return Arrays.asList("drop table if exists t_gson_entity", "CREATE TABLE IF NOT EXISTS t_gson_entity (" +
+            "id VARCHAR(32) NOT NULL," +
+            "name VARCHAR(30) NULL DEFAULT NULL," +
+            "card VARCHAR(255) NULL DEFAULT NULL," +
+            "attr VARCHAR(255) NULL DEFAULT NULL," +
+            "attr2 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr3 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr4 VARCHAR(255) NULL DEFAULT NULL," +
+            "PRIMARY KEY (id))");
+    }
+
+    @Test
+    void test() {
+        doTest(mapper -> {
+            var entity = new GsonEntity("123", "秋秋", new GsonEntity.Card("1", "1111"),
+                Arrays.asList(new GsonEntity.Attr("age", "18"), new GsonEntity.Attr("sex", "女")),
+                new HashMap<>(Map.of("test", new GsonEntity.Attr("小红", "1"))),
+                new HashMap<>(Map.of("name", "1", "test2", "2")),
+                new HashMap<>(Map.of("test1", "1", "test2", 2))
+            );
+            mapper.insert(entity);
+            GsonEntity jackJsonEntity = mapper.selectById(entity.getId());
+            Assertions.assertEquals("秋秋", jackJsonEntity.getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());
+            Assertions.assertNotNull(jackJsonEntity.getCard());
+            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());
+            Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());
+            Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name"));
+            Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2"));
+            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());
+            //TODO 反序列化精度问题
+            Assertions.assertEquals(2.0, jackJsonEntity.getAttr4().get("test2"));
+        });
+
+    }
+
+
+}

+ 67 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntity.java

@@ -0,0 +1,67 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName(value = "t_jackson_entity", autoResultMap = true)
+public class JackJsonEntity {
+
+    @TableId(type = IdType.INPUT)
+    private String id;
+
+    private String name;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Card card;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private List<Attr> attr;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, Attr> attr2;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, String> attr3;
+
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, Object> attr4;
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Attr {
+
+        private String name;
+
+        private String value;
+
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Card {
+
+        private String id;
+
+        private String value;
+
+    }
+
+
+}

+ 10 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntityMapper.java

@@ -0,0 +1,10 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public interface JackJsonEntityMapper extends BaseMapper<JackJsonEntity> {
+
+}

+ 56 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JacksonTest.java

@@ -0,0 +1,56 @@
+package com.baomidou.mybatisplus.test.json;
+
+import com.baomidou.mybatisplus.test.BaseDbTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nieqiurong 2024年3月4日
+ */
+public class JacksonTest extends BaseDbTest<JackJsonEntityMapper> {
+
+    @Override
+    protected List<String> tableSql() {
+        return Arrays.asList("drop table if exists t_jackson_entity", "CREATE TABLE IF NOT EXISTS t_jackson_entity (" +
+            "id VARCHAR(32) NOT NULL," +
+            "name VARCHAR(30) NULL DEFAULT NULL," +
+            "card VARCHAR(255) NULL DEFAULT NULL," +
+            "attr VARCHAR(255) NULL DEFAULT NULL," +
+            "attr2 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr3 VARCHAR(255) NULL DEFAULT NULL," +
+            "attr4 VARCHAR(255) NULL DEFAULT NULL," +
+            "PRIMARY KEY (id))");
+    }
+
+    @Test
+    void test() {
+        doTest(mapper -> {
+            var entity = new JackJsonEntity("123", "秋秋", new JackJsonEntity.Card("1", "1111"),
+                Arrays.asList(new JackJsonEntity.Attr("age", "18"), new JackJsonEntity.Attr("sex", "女")),
+                new HashMap<>(Map.of("test", new JackJsonEntity.Attr("小红", "1"))),
+                new HashMap<>(Map.of("name", "1", "test2", "2")),
+                new HashMap<>(Map.of("test1", "1", "test2", 2))
+            );
+            mapper.insert(entity);
+            JackJsonEntity jackJsonEntity = mapper.selectById(entity.getId());
+            Assertions.assertEquals("秋秋", jackJsonEntity.getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());
+            Assertions.assertNotNull(jackJsonEntity.getCard());
+            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());
+            Assertions.assertEquals("小红", jackJsonEntity.getAttr2().get("test").getName());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());
+            Assertions.assertEquals("1", jackJsonEntity.getAttr3().get("name"));
+            Assertions.assertEquals("2", jackJsonEntity.getAttr3().get("test2"));
+            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());
+            Assertions.assertEquals(2, jackJsonEntity.getAttr4().get("test2"));
+        });
+
+    }
+
+
+}

+ 22 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/Entity.java

@@ -9,6 +9,8 @@ import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
 
 import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author miemie
@@ -28,10 +30,30 @@ public class Entity implements Serializable {
     @TableField(typeHandler = GsonTypeHandler.class)
     private Gg gg2;
 
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private List<Gg> gg3;
+
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private List<Gg4> gg4;
+
+    @TableField(typeHandler = GsonTypeHandler.class)
+    private String[] str;
+
+
     @Data
     @NoArgsConstructor
     @AllArgsConstructor
     public static class Gg {
         private String name;
     }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class Gg4 {
+        private String name;
+        private Gg gg;
+        private List<Gg> ggList;
+        private Map<String,Gg> ggMap;
+    }
 }

+ 21 - 1
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/ResultMapTest.java

@@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -17,7 +18,11 @@ public class ResultMapTest extends BaseDbTest<EntityMapper> {
     @Test
     void test() {
         doTestAutoCommit(m -> m.insert(new Entity().setGg1(new Entity.Gg("老王"))
-            .setGg2(new Entity.Gg("老李"))));
+            .setStr(new String[]{"hello"})
+            .setGg2(new Entity.Gg("老李"))
+            .setGg3(List.of(new Entity.Gg("老张")))
+            .setGg4(List.of(new Entity.Gg4("秋秋", new Entity.Gg("小红"), List.of(new Entity.Gg("小猫")), Map.of("test", new Entity.Gg("小明")))))
+        ));
 
         doTest(m -> {
             Entity entity = m.selectOne(null);
@@ -27,6 +32,18 @@ public class ResultMapTest extends BaseDbTest<EntityMapper> {
 
             assertThat(entity.getGg2()).as("typeHandler正常").isNotNull();
             assertThat(entity.getGg2().getName()).as("是老李").isEqualTo("老李");
+
+            assertThat(entity.getGg3()).as("typeHandler正常").isNotNull();
+            assertThat(entity.getGg3().getFirst().getName()).as("是老张").isEqualTo("老张");
+
+            assertThat(entity.getGg4()).as("typeHandler正常").isNotNull();
+            assertThat(entity.getGg4().getFirst().getName()).as("是秋秋").isEqualTo("秋秋");
+            assertThat(entity.getGg4().getFirst().getGg().getName()).as("是小红").isEqualTo("小红");
+            assertThat(entity.getGg4().getFirst().getGgList().getFirst().getName()).as("是小猫").isEqualTo("小猫");
+            assertThat(entity.getGg4().getFirst().getGgMap().get("test").getName()).as("是小明").isEqualTo("小明");
+
+            assertThat(entity.getStr()).as("typeHandler正常").isNotNull();
+            assertThat(entity.getStr()[0]).isEqualTo("hello");
         });
     }
 
@@ -37,6 +54,9 @@ public class ResultMapTest extends BaseDbTest<EntityMapper> {
                 "id BIGINT(20) NOT NULL,\n" +
                 "gg1 VARCHAR(255) NULL DEFAULT NULL,\n" +
                 "gg2 VARCHAR(255) NULL DEFAULT NULL,\n" +
+                "gg3 VARCHAR(255) NULL DEFAULT NULL,\n" +
+                "gg4 VARCHAR(255) NULL DEFAULT NULL,\n" +
+                "str VARCHAR(255) NULL DEFAULT NULL,\n" +
                 "PRIMARY KEY (id)" +
                 ")");
     }

+ 3 - 0
mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/resultmap/EntityMapper.xml

@@ -9,5 +9,8 @@
 
     <resultMap id="resultMap" type="com.baomidou.mybatisplus.test.resultmap.Entity" extends="baseResult">
         <result column="gg2" property="gg2" typeHandler="com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler"/>
+        <result property="gg3" column="gg3" typeHandler="com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler"/>
+        <result property="gg4" column="gg4" javaType="list" typeHandler="com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler"/>
+        <result property="str" column="str" typeHandler="com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler"/>
     </resultMap>
 </mapper>

+ 1 - 1
spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java

@@ -75,7 +75,7 @@ class GeneratePomTest {
             Dependency bom = dependenciesMap.get("spring-boot-dependencies");
             Assertions.assertEquals("import", bom.getScope());
             Assertions.assertFalse(bom.isOptional());
-            Assertions.assertEquals(dependenciesMap.get("spring-cloud-commons").getVersion(), "3.1.1");
+            Assertions.assertEquals(dependenciesMap.get("spring-cloud-commons").getVersion(), "3.1.8");
             Assertions.assertEquals(dependenciesMap.get("mybatis-spring").getVersion(), "2.1.2");
             Assertions.assertEquals(dependenciesMap.get("spring-boot-dependencies").getVersion(), "2.7.15");
         }

+ 1 - 1
spring-boot-starter/mybatis-plus-spring-boot3-starter/build.gradle

@@ -16,7 +16,7 @@ dependencies {
     implementation "${lib['mybatis-thymeleaf']}"
     implementation "${lib.'mybatis-velocity'}"
     implementation "${lib.'mybatis-freemarker'}"
-    implementation "org.springframework.cloud:spring-cloud-commons:4.0.4"
+    implementation "org.springframework.cloud:spring-cloud-commons:4.1.1"
     testImplementation "org.springframework.boot:spring-boot-starter-test"
     testImplementation "org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3"
     testImplementation "${lib.h2}"

+ 1 - 1
spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java

@@ -75,7 +75,7 @@ class GeneratePomTest {
             Dependency bom = dependenciesMap.get("spring-boot-dependencies");
             Assertions.assertEquals("import", bom.getScope());
             Assertions.assertFalse(bom.isOptional());
-            Assertions.assertEquals(dependenciesMap.get("spring-cloud-commons").getVersion(), "4.0.4");
+            Assertions.assertEquals(dependenciesMap.get("spring-cloud-commons").getVersion(), "4.1.1");
             Assertions.assertEquals(dependenciesMap.get("mybatis-spring").getVersion(), "3.0.3");
             Assertions.assertEquals(dependenciesMap.get("spring-boot-dependencies").getVersion(), "3.2.0");
         }