Ver código fonte

修复MyBatis mapper插入TypeHandler处理异常问题

woody 8 meses atrás
pai
commit
2263c759d1

+ 33 - 7
framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java

@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.core.metadata.TableInfo;
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.database.context.DatabaseContextHolder;
 import com.google.common.collect.Lists;
 import javassist.ClassPool;
 import javassist.CtClass;
@@ -50,6 +51,7 @@ public abstract class MybatisConfigurer {
             pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
             initializeMapperListener(pool);
             initializeTableInfoListener(pool);
+            initializeUnknownTypeHandler(pool);
             INSTANCES.addAll(initializeInterceptorInstances(configuration));
             INSTANCES.trimToSize();
         } catch (Exception e) {
@@ -91,21 +93,22 @@ public abstract class MybatisConfigurer {
      */
     private static void initializeTableInfoListener(ClassPool pool) throws Exception {
         // 获取原始initTableFields方法
-        CtMethod initTableFields = null;
+        CtMethod method = null;
         CtClass clazz = pool.get("com.baomidou.mybatisplus.core.metadata.TableInfoHelper");
-        for (CtMethod method : clazz.getDeclaredMethods()) {
-            if (method.getName().equals("initTableFields")) {
-                initTableFields = method;
+        for (CtMethod m : clazz.getDeclaredMethods()) {
+            if (m.getName().equals("initTableFields")) {
+                method = m;
+                break;
             }
         }
-        if (initTableFields == null) {
+        if (method == null) {
             throw new NotFoundException("initTableFields");
         }
 
         // 重命名initTableFields方法名
-        CtMethod copy = CtNewMethod.copy(initTableFields, clazz, null);
+        CtMethod copy = CtNewMethod.copy(method, clazz, null);
         copy.setName("doInitTableFields");
-        clazz.removeMethod(initTableFields);
+        clazz.removeMethod(method);
         clazz.addMethod(copy);
 
         // 替换initTableFields方法,加入表信息初始化回调逻辑
@@ -117,6 +120,29 @@ public abstract class MybatisConfigurer {
         clazz.toClass();
     }
 
+    /**
+     * 初始化对象未知类型处理器实例
+     *
+     * @param pool 类对象池
+     * @throws Exception 初始化异常
+     */
+    private static void initializeUnknownTypeHandler(ClassPool pool) throws Exception {
+        CtClass clazz = pool.get("org.apache.ibatis.mapping.ParameterMapping");
+        CtClass inner = clazz.getNestedClasses()[1];
+        CtMethod method = inner.getDeclaredMethod("build");
+        method.setBody(String.format("{\n" +
+                "this.resolveTypeHandler();\n" +
+                "if (this.parameterMapping.typeHandler == null && this.parameterMapping.javaType != null " +
+                "&& !%s.isTypeHandlerRegistered(this.parameterMapping.javaType)) {\n" +
+                "this.parameterMapping.typeHandler = %s.getUnknownTypeHandler(this.parameterMapping.configuration);\n" +
+                "}\n " +
+                "this.validate();\n" +
+                "return this.parameterMapping;\n" +
+                "}", DatabaseContextHolder.class.getName(), DatabaseContextHolder.class.getName()));
+        inner.toClass();
+        clazz.toClass();
+    }
+
     /**
      * 初始化Mybatis配置拦截器实例
      *

+ 3 - 41
framework-database/src/main/java/com/chelvc/framework/database/config/TypeHandlerConfigurer.java

@@ -1,30 +1,14 @@
 package com.chelvc.framework.database.config;
 
-import java.io.InputStream;
-import java.io.Reader;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.Month;
-import java.time.OffsetDateTime;
-import java.time.OffsetTime;
-import java.time.Year;
-import java.time.YearMonth;
-import java.time.ZonedDateTime;
-import java.time.chrono.JapaneseDate;
 import java.util.Collection;
-import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -175,32 +159,10 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @return true/false
      */
     private boolean isMetaType(Class<?> type) {
-        return this.isTypeHandlerRegistered(type) || Map.class.isAssignableFrom(type)
+        return DatabaseContextHolder.isTypeHandlerRegistered(type) || Map.class.isAssignableFrom(type)
                 || Collection.class.isAssignableFrom(type);
     }
 
-    /**
-     * 判断对象类型处理器是否已注册
-     *
-     * @param type 对象类型
-     * @return true/false
-     */
-    private boolean isTypeHandlerRegistered(Class<?> type) {
-        return type == byte.class || type == Byte.class || type == byte[].class || type == Byte[].class
-                || type == char.class || type == Character.class || type == short.class || type == Short.class
-                || type == int.class || type == Integer.class || type == long.class || type == Long.class
-                || type == float.class || type == Float.class || type == double.class || type == Double.class
-                || type == boolean.class || type == Boolean.class || type == String.class || type == Object.class
-                || type == BigInteger.class || type == BigDecimal.class || type == Reader.class
-                || type == InputStream.class || type == Date.class || type == java.sql.Date.class
-                || type == java.sql.Time.class || type == java.sql.Timestamp.class || type == Instant.class
-                || type == LocalDate.class || type == LocalTime.class || type == LocalDateTime.class
-                || type == OffsetTime.class || type == OffsetDateTime.class || type == ZonedDateTime.class
-                || type == Year.class || type == Month.class || type == YearMonth.class || type == JapaneseDate.class
-                || (type != null && Enum.class.isAssignableFrom(type)) || type == File.class || type == Period.class
-                || type == Region.class || type == Modification.class;
-    }
-
     /**
      * 判断对象是否可配置类型处理器
      *
@@ -224,7 +186,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
 
                     // 是否是JSON字段
                     Type type = TypeParameterResolver.resolveFieldType(field, clazz);
-                    if (!this.isTypeHandlerRegistered(ObjectUtils.type2class(type))) {
+                    if (!DatabaseContextHolder.isTypeHandlerRegistered(ObjectUtils.type2class(type))) {
                         return true;
                     }
                 }
@@ -383,7 +345,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
         if (((handler == null && (handler = (Class<? extends TypeHandler<?>>)
                 ObjectUtils.ifNull(annotation, TableField::typeHandler)) == null)
                 || handler == UnknownTypeHandler.class || handler == JacksonTypeHandler.class)
-                && !this.isTypeHandlerRegistered(model)) {
+                && !DatabaseContextHolder.isTypeHandlerRegistered(model)) {
             handler = this.lookupJsonHandlerClass(type);
         }
 

+ 65 - 0
framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java

@@ -1,12 +1,28 @@
 package com.chelvc.framework.database.context;
 
+import java.io.InputStream;
+import java.io.Reader;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+import java.time.chrono.JapaneseDate;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -29,8 +45,12 @@ import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.function.Executor;
 import com.chelvc.framework.common.function.Handler;
 import com.chelvc.framework.common.function.Provider;
+import com.chelvc.framework.common.model.File;
+import com.chelvc.framework.common.model.Modification;
 import com.chelvc.framework.common.model.Pagination;
 import com.chelvc.framework.common.model.Paging;
+import com.chelvc.framework.common.model.Period;
+import com.chelvc.framework.common.model.Region;
 import com.chelvc.framework.common.util.AssertUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
@@ -39,7 +59,9 @@ import com.chelvc.framework.database.entity.Entity;
 import com.google.common.collect.Maps;
 import lombok.NonNull;
 import org.apache.ibatis.reflection.property.PropertyNamer;
+import org.apache.ibatis.session.Configuration;
 import org.apache.ibatis.type.TypeHandler;
+import org.apache.ibatis.type.UnknownTypeHandler;
 import org.mybatis.spring.SqlSessionTemplate;
 import org.springframework.context.ApplicationContext;
 import org.springframework.dao.DuplicateKeyException;
@@ -87,6 +109,10 @@ public final class DatabaseContextHolder {
      */
     private static volatile DatabaseCryptoContext CRYPTO_CONTEXT;
 
+    /**
+     * 未知类型处理器实例
+     */
+    private static volatile UnknownTypeHandler UNKNOWN_TYPE_HANDLER;
 
     private DatabaseContextHolder() {
     }
@@ -428,6 +454,28 @@ public final class DatabaseContextHolder {
         return column.getColumn();
     }
 
+    /**
+     * 判断对象类型处理器是否已注册
+     *
+     * @param type 对象类型
+     * @return true/false
+     */
+    public static boolean isTypeHandlerRegistered(Class<?> type) {
+        return type == byte.class || type == Byte.class || type == byte[].class || type == Byte[].class
+                || type == char.class || type == Character.class || type == short.class || type == Short.class
+                || type == int.class || type == Integer.class || type == long.class || type == Long.class
+                || type == float.class || type == Float.class || type == double.class || type == Double.class
+                || type == boolean.class || type == Boolean.class || type == String.class || type == Object.class
+                || type == BigInteger.class || type == BigDecimal.class || type == Reader.class
+                || type == InputStream.class || type == Date.class || type == java.sql.Date.class
+                || type == java.sql.Time.class || type == java.sql.Timestamp.class || type == Instant.class
+                || type == LocalDate.class || type == LocalTime.class || type == LocalDateTime.class
+                || type == OffsetTime.class || type == OffsetDateTime.class || type == ZonedDateTime.class
+                || type == Year.class || type == Month.class || type == YearMonth.class || type == JapaneseDate.class
+                || (type != null && Enum.class.isAssignableFrom(type)) || type == File.class || type == Period.class
+                || type == Region.class || type == Modification.class;
+    }
+
     /**
      * 初始化TypeHandler实例
      *
@@ -446,6 +494,23 @@ public final class DatabaseContextHolder {
         });
     }
 
+    /**
+     * 获取未知类型处理器实例
+     *
+     * @param configuration MyBatis配置信息
+     * @return 未知类型处理器实例
+     */
+    public static UnknownTypeHandler getUnknownTypeHandler(@NonNull Configuration configuration) {
+        if (UNKNOWN_TYPE_HANDLER == null) {
+            synchronized (DatabaseContextHolder.class) {
+                if (UNKNOWN_TYPE_HANDLER == null) {
+                    UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(configuration);
+                }
+            }
+        }
+        return UNKNOWN_TYPE_HANDLER;
+    }
+
     /**
      * 根据包名查找数据模型对象
      *