فهرست منبع

优化地区处理逻辑;修复数据库JSON字段批量操作异常问题;

woody 8 ماه پیش
والد
کامیت
7a9df1ed5e

+ 20 - 20
framework-common/src/main/java/com/chelvc/framework/common/model/Region.java

@@ -64,27 +64,27 @@ public final class Region implements Serializable {
     }
 
     /**
-     * 判断是否是级编码
+     * 判断是否是级编码
      *
      * @param code 地区编码
      * @return true/false
      */
-    public static boolean isCity(Number code) {
-        if (!isRegion(code)) {
-            return false;
-        }
-        int value = code.intValue();
-        return value % 10000 > 0 && value % 100 == 0;
+    public static boolean isProvincial(Number code) {
+        return isRegion(code) && code.intValue() % 10000 == 0;
     }
 
     /**
-     * 判断是否是级编码
+     * 判断是否是市级编码
      *
      * @param code 地区编码
      * @return true/false
      */
-    public static boolean isProvince(Number code) {
-        return isRegion(code) && code.intValue() % 10000 == 0;
+    public static boolean isMunicipal(Number code) {
+        if (!isRegion(code)) {
+            return false;
+        }
+        int value = code.intValue();
+        return value % 10000 > 0 && value % 100 == 0;
     }
 
     /**
@@ -261,9 +261,9 @@ public final class Region implements Serializable {
             return null;
         }
         int value = code.intValue();
-        if (isProvince(value)) {
+        if (isProvincial(value)) {
             return new Region(value, value + 9999);
-        } else if (isCity(value)) {
+        } else if (isMunicipal(value)) {
             return new Region(value, value + 99);
         }
         return new Region(value, 0);
@@ -276,7 +276,7 @@ public final class Region implements Serializable {
      * @return 地区实例
      */
     public static Region city(Number code) {
-        if (!isRegion(code) || isProvince(code)) {
+        if (!isRegion(code) || isProvincial(code)) {
             return null;
         }
         int value = code2city(code.intValue());
@@ -344,7 +344,7 @@ public final class Region implements Serializable {
      */
     @JsonIgnore
     public Region getParent() {
-        if (this.isCity()) {
+        if (this.isMunicipal()) {
             return province(this.code);
         } else if (this.isDistrict()) {
             return city(this.code);
@@ -373,23 +373,23 @@ public final class Region implements Serializable {
     }
 
     /**
-     * 判断是否是级地区
+     * 判断是否是级地区
      *
      * @return true/false
      */
     @JsonIgnore
-    public boolean isCity() {
-        return isCity(this.code);
+    public boolean isProvincial() {
+        return isProvincial(this.code);
     }
 
     /**
-     * 判断是否是级地区
+     * 判断是否是级地区
      *
      * @return true/false
      */
     @JsonIgnore
-    public boolean isProvince() {
-        return isProvince(this.code);
+    public boolean isMunicipal() {
+        return isMunicipal(this.code);
     }
 
     /**

+ 3 - 5
framework-database/src/main/java/com/chelvc/framework/database/config/DatabaseConfigurer.java

@@ -46,11 +46,6 @@ import org.springframework.transaction.annotation.Transactional;
 @Configuration
 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
 public class DatabaseConfigurer {
-    static {
-        // 初始化Mybatis配置拦截器
-        MybatisConfigurer.initialize();
-    }
-
     private final DatabaseProperties properties;
     private final ApplicationContext applicationContext;
 
@@ -125,6 +120,9 @@ public class DatabaseConfigurer {
     @Bean
     public ConfigurationCustomizer configurationCustomizer() {
         return configuration -> {
+            // 初始化Mybatis配置拦截器
+            MybatisConfigurer.initialize(configuration);
+
             // 注册自定义字段类型处理器
             TypeHandlerRegistry handlerRegistry = configuration.getTypeHandlerRegistry();
             handlerRegistry.register(File.class, JdbcType.VARCHAR, new FileTypeHandler());

+ 17 - 4
framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java

@@ -1,5 +1,6 @@
 package com.chelvc.framework.database.config;
 
+import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -18,6 +19,7 @@ import javassist.LoaderClassPath;
 import javassist.NotFoundException;
 import lombok.NonNull;
 import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.session.Configuration;
 
 /**
  * Mybatis配置类
@@ -31,16 +33,24 @@ public abstract class MybatisConfigurer {
      */
     private static final ArrayList<MybatisConfigurer> INSTANCES = Lists.newArrayList();
 
+    protected final Configuration configuration;
+
+    public MybatisConfigurer(@NonNull Configuration configuration) {
+        this.configuration = configuration;
+    }
+
     /**
      * 初始化配置拦截器
+     *
+     * @param configuration MyBatis配置信息
      */
-    public static void initialize() {
+    public static void initialize(Configuration configuration) {
         try {
             ClassPool pool = ClassPool.getDefault();
             pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
             initializeMapperListener(pool);
             initializeTableInfoListener(pool);
-            INSTANCES.addAll(initializeInterceptorInstances());
+            INSTANCES.addAll(initializeInterceptorInstances(configuration));
             INSTANCES.trimToSize();
         } catch (Exception e) {
             throw new RuntimeException(e);
@@ -110,10 +120,12 @@ public abstract class MybatisConfigurer {
     /**
      * 初始化Mybatis配置拦截器实例
      *
+     * @param configuration MyBatis配置信息
      * @return Mybatis配置拦截器列表
      * @throws Exception 初始化异常
      */
-    private static List<MybatisConfigurer> initializeInterceptorInstances() throws Exception {
+    private static List<MybatisConfigurer> initializeInterceptorInstances(Configuration configuration)
+            throws Exception {
         // 查找当前包目录下MybatisConfigureInterceptor实现
         List<Class<?>> classes = SpringUtils.lookupClasses(
                 MybatisConfigurer.class.getPackage().getName(),
@@ -129,7 +141,8 @@ public abstract class MybatisConfigurer {
         // 初始化对象实例
         List<MybatisConfigurer> interceptors = Lists.newArrayListWithCapacity(classes.size());
         for (Class<?> clazz : classes) {
-            interceptors.add((MybatisConfigurer) clazz.newInstance());
+            Constructor<?> constructor = clazz.getConstructor(Configuration.class);
+            interceptors.add((MybatisConfigurer) constructor.newInstance(configuration));
         }
         return interceptors;
     }

+ 45 - 14
framework-database/src/main/java/com/chelvc/framework/database/config/TypeHandlerConfigurer.java

@@ -104,8 +104,10 @@ import javassist.NotFoundException;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.parsing.XNode;
 import org.apache.ibatis.reflection.TypeParameterResolver;
+import org.apache.ibatis.session.Configuration;
 import org.apache.ibatis.type.JdbcType;
 import org.apache.ibatis.type.TypeAliasRegistry;
+import org.apache.ibatis.type.TypeHandler;
 import org.apache.ibatis.type.UnknownTypeHandler;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -120,14 +122,18 @@ import org.w3c.dom.NodeList;
 @Slf4j
 public class TypeHandlerConfigurer extends MybatisConfigurer {
     /**
-     * JSON类型/处理器映射表
+     * 内置的别名/对象类型映射表
      */
-    private final Map<Type, Class<?>> handlers = Maps.newConcurrentMap();
+    private final Map<String, Class<?>> aliases = new TypeAliasRegistry().getTypeAliases();
 
     /**
-     * 内置的别名/对象类型映射表
+     * JSON类型/处理器映射表
      */
-    private final Map<String, Class<?>> aliases = new TypeAliasRegistry().getTypeAliases();
+    private final Map<Type, Class<? extends TypeHandler<?>>> handlers = Maps.newConcurrentMap();
+
+    public TypeHandlerConfigurer(Configuration configuration) {
+        super(configuration);
+    }
 
     /**
      * 将对象名称转换成类对象
@@ -236,12 +242,19 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @param clazz 对象类型
      * @param field 表字段信息
      */
+    @SuppressWarnings("unchecked")
     private void bindTypeHandler(Class<?> clazz, TableFieldInfo field) {
-        Class<?> handler = this.lookupTypeHandlerClass(clazz, field.getField(), field.getTypeHandler());
+        Class<? extends TypeHandler<?>> handler =
+                this.lookupTypeHandlerClass(clazz, field.getField(), field.getTypeHandler());
         if (handler != null) {
             ObjectUtils.setValue(field, "typeHandler", handler);
             String el = ObjectUtils.getValue(field, "el") + ",typeHandler=" + handler.getName();
             ObjectUtils.setValue(field, "el", el);
+
+            // 注册类型TypeHandler实例
+            this.configuration.getTypeHandlerRegistry().register(
+                    (Class<Object>) field.getField().getType(), DatabaseContextHolder.initializeTypeHandler(handler)
+            );
         }
     }
 
@@ -251,7 +264,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @param type 对象类型
      * @return 类型处理器对象
      */
-    private Class<?> lookupJsonHandlerClass(Type type) {
+    private Class<? extends TypeHandler<?>> lookupJsonHandlerClass(Type type) {
         if (ObjectUtils.isOnlyMap(type)) {
             return MapTypeHandler.class;
         } else if (ObjectUtils.isOnlySet(type)) {
@@ -353,7 +366,9 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @param handler 当前类型处理器对象
      * @return 类型处理器对象
      */
-    private Class<?> lookupTypeHandlerClass(Class<?> clazz, Field field, Class<?> handler) {
+    @SuppressWarnings("unchecked")
+    private Class<? extends TypeHandler<?>> lookupTypeHandlerClass(Class<?> clazz, Field field,
+                                                                   Class<? extends TypeHandler<?>> handler) {
         // 排除忽略字段
         TableField annotation = field.getAnnotation(TableField.class);
         if (annotation != null && !annotation.exist()) {
@@ -363,7 +378,8 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
         // 如果当前类型处理器不存在,则继续查找@TableField注解中的类型处理器,如果仍不存在则尝试使用JSON类型处理器
         Type type = TypeParameterResolver.resolveFieldType(field, clazz);
         Class<?> model = ObjectUtils.type2class(type);
-        if (((handler == null && (handler = ObjectUtils.ifNull(annotation, TableField::typeHandler)) == null)
+        if (((handler == null && (handler = (Class<? extends TypeHandler<?>>)
+                ObjectUtils.ifNull(annotation, TableField::typeHandler)) == null)
                 || handler == UnknownTypeHandler.class || handler == JacksonTypeHandler.class)
                 && !this.isTypeHandlerRegistered(model)) {
             handler = this.lookupJsonHandlerClass(type);
@@ -391,7 +407,8 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @param type 对象类型
      * @return 类型处理器对象
      */
-    private Class<?> initializeJsonHandlerClass(Type type) {
+    @SuppressWarnings("unchecked")
+    private Class<? extends TypeHandler<?>> initializeJsonHandlerClass(Type type) {
         ClassPool pool = ClassPool.getDefault();
         CtClass handler = pool.makeClass(StringUtils.uuid());
         try {
@@ -399,7 +416,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
             CtConstructor constructor = new CtConstructor(new CtClass[]{}, handler);
             constructor.setBody(String.format("{super(%s);}", ObjectUtils.analyse(type)));
             handler.addConstructor(constructor);
-            return handler.toClass(this.getClass().getClassLoader(), null);
+            return (Class<? extends TypeHandler<?>>) handler.toClass(this.getClass().getClassLoader(), null);
         } catch (NotFoundException | CannotCompileException e) {
             throw new RuntimeException(e);
         }
@@ -412,7 +429,8 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @param superclass 处理器父类
      * @return 类型处理器对象
      */
-    private Class<?> initializeSensitiveHandlerClass(Class<?> model, Class<?> superclass) {
+    @SuppressWarnings("unchecked")
+    private Class<? extends TypeHandler<?>> initializeSensitiveHandlerClass(Class<?> model, Class<?> superclass) {
         if (superclass == null) {
             return SensitiveTypeHandler.class;
         }
@@ -455,7 +473,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
                     model.getName(), method, CallableStatement.class.getName(), SQLException.class.getName(),
                     method, CallableStringDecrypter.class.getName()
             ), handler));
-            return handler.toClass(this.getClass().getClassLoader(), null);
+            return (Class<? extends TypeHandler<?>>) handler.toClass(this.getClass().getClassLoader(), null);
         } catch (NotFoundException | CannotCompileException e) {
             throw new RuntimeException(e);
         }
@@ -488,6 +506,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
      * @param field    对象字段
      * @param consumer resultMap元素回调函数
      */
+    @SuppressWarnings("unchecked")
     private void initializeResultMapping(Document document, Class<?> clazz, Field field, Consumer<Element> consumer) {
         // 排除主键字段
         if (field.isAnnotationPresent(TableId.class)) {
@@ -495,7 +514,7 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
         }
 
         // 排除不需要TypeHandler的字段
-        Class<?> handler = this.lookupTypeHandlerClass(clazz, field, null);
+        Class<? extends TypeHandler<?>> handler = this.lookupTypeHandlerClass(clazz, field, null);
         if (handler == null) {
             return;
         }
@@ -524,9 +543,15 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
             child.setAttribute("typeHandler", handler.getName());
             consumer.accept(child);
         });
+
+        // 注册类型TypeHandler实例
+        this.configuration.getTypeHandlerRegistry().register(
+                (Class<Object>) field.getType(), DatabaseContextHolder.initializeTypeHandler(handler)
+        );
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public void intercept(XNode mapper) {
         // 设置已有resultMap的类型处理器
         Document document = mapper.getNode().getOwnerDocument();
@@ -547,9 +572,15 @@ public class TypeHandlerConfigurer extends MybatisConfigurer {
                     }
 
                     // 重置字段类型处理器
-                    Class<?> handler = this.name2class(child.getStringAttribute("typeHandler"));
+                    Class<? extends TypeHandler<?>> handler =
+                            (Class<? extends TypeHandler<?>>) this.name2class(child.getStringAttribute("typeHandler"));
                     if ((handler = this.lookupTypeHandlerClass(clazz, field, handler)) != null) {
                         ((Element) child.getNode()).setAttribute("typeHandler", handler.getName());
+
+                        // 注册类型TypeHandler实例
+                        this.configuration.getTypeHandlerRegistry().register(
+                                (Class<Object>) field.getType(), DatabaseContextHolder.initializeTypeHandler(handler)
+                        );
                     }
                     if (StringUtils.isEmpty(child.getStringAttribute("typeHandler"))) {
                         continue;

+ 1 - 6
framework-database/src/main/java/com/chelvc/framework/database/handler/JsonTypeHandler.java

@@ -10,7 +10,6 @@ import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Types;
 import java.util.List;
 import java.util.Set;
 
@@ -290,11 +289,7 @@ public interface JsonTypeHandler<T> extends TypeHandler<T> {
      * @throws SQLException SQL异常
      */
     default void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
-        if (ObjectUtils.isBlank(parameter)) {
-            ps.setNull(i, Types.VARCHAR);
-        } else {
-            ps.setString(i, JacksonUtils.serialize(mapper, parameter));
-        }
+        ps.setString(i, JacksonUtils.serialize(mapper, parameter));
     }
 
     /**

+ 3 - 2
framework-location/src/main/java/com/chelvc/framework/location/support/TencentLocationHandler.java

@@ -1,5 +1,6 @@
 package com.chelvc.framework.location.support;
 
+import java.util.Objects;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -71,7 +72,7 @@ public class TencentLocationHandler implements LocationHandler {
         }
 
         // 构建地址信息
-        Region region = Region.of(position.getAdcode());
+        Region region = Objects.requireNonNull(Region.of(position.getAdcode()));
         TencentLocationResponse.Location location = response.getResult().getLocation();
         Double longitude = ObjectUtils.ifNull(location, TencentLocationResponse.Location::getLongitude);
         Double latitude = ObjectUtils.ifNull(location, TencentLocationResponse.Location::getLatitude);
@@ -82,7 +83,7 @@ public class TencentLocationHandler implements LocationHandler {
                 ));
         address.setProvince(Area.builder().id(Region.code2province(position.getAdcode()))
                 .name(position.getProvince()).build());
-        if (!region.isProvince()) {
+        if (!region.isProvincial()) {
             address.setCity(Area.builder().id(Region.code2city(region.getCode())).name(position.getCity()).build());
         }
         if (region.isDistrict()) {