|
@@ -1,393 +0,0 @@
|
|
|
-package com.chelvc.framework.database.interceptor;
|
|
|
-
|
|
|
-import java.lang.reflect.Field;
|
|
|
-import java.lang.reflect.ParameterizedType;
|
|
|
-import java.lang.reflect.Type;
|
|
|
-import java.util.Collection;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.Set;
|
|
|
-import java.util.concurrent.atomic.AtomicLong;
|
|
|
-
|
|
|
-import com.baomidou.mybatisplus.annotation.TableField;
|
|
|
-import com.baomidou.mybatisplus.annotation.TableId;
|
|
|
-import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
|
|
-import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
|
|
-import com.chelvc.framework.common.model.File;
|
|
|
-import com.chelvc.framework.common.model.Modification;
|
|
|
-import com.chelvc.framework.common.model.Period;
|
|
|
-import com.chelvc.framework.common.model.Reference;
|
|
|
-import com.chelvc.framework.common.model.Region;
|
|
|
-import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
-import com.chelvc.framework.common.util.StringUtils;
|
|
|
-import com.chelvc.framework.database.context.DatabaseContextHolder;
|
|
|
-import com.chelvc.framework.database.handler.BooleansTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.DoublesTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.FilesTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.FloatsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.IntegersTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.JsonTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.ListTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.ListsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.LongsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.MapTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.MapsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.ModificationsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.PeriodsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.RegionsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.SetTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.SetsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.ShortsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.StringsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueBooleansTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueDoublesTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueFilesTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueFloatsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueIntegersTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueLongsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueModificationsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniquePeriodsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueRegionsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueShortsTypeHandler;
|
|
|
-import com.chelvc.framework.database.handler.UniqueStringsTypeHandler;
|
|
|
-import com.google.common.collect.Maps;
|
|
|
-import javassist.ClassPool;
|
|
|
-import javassist.CtClass;
|
|
|
-import javassist.CtConstructor;
|
|
|
-import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.apache.ibatis.parsing.XNode;
|
|
|
-import org.apache.ibatis.type.TypeAliasRegistry;
|
|
|
-import org.apache.ibatis.type.UnknownTypeHandler;
|
|
|
-import org.w3c.dom.Document;
|
|
|
-import org.w3c.dom.Element;
|
|
|
-
|
|
|
-/**
|
|
|
- * JSON类型字段处理器配置拦截器
|
|
|
- *
|
|
|
- * @author Woody
|
|
|
- * @date 2024/4/6
|
|
|
- */
|
|
|
-@Slf4j
|
|
|
-public class JsonHandlerConfigureInterceptor extends MybatisConfigureInterceptor {
|
|
|
- /**
|
|
|
- * JSON类型处理器计数器
|
|
|
- */
|
|
|
- private final AtomicLong counter = new AtomicLong(0);
|
|
|
-
|
|
|
- /**
|
|
|
- * JSON类型/处理器映射表
|
|
|
- */
|
|
|
- private final Map<Type, Class<?>> handlers = Maps.newConcurrentMap();
|
|
|
-
|
|
|
- /**
|
|
|
- * 内置的别名/对象类型映射表
|
|
|
- */
|
|
|
- private final Map<String, Class<?>> aliases = new TypeAliasRegistry().getTypeAliases();
|
|
|
-
|
|
|
- /**
|
|
|
- * 判断是否是元类型
|
|
|
- *
|
|
|
- * @param clazz 对象类型
|
|
|
- * @return true/false
|
|
|
- */
|
|
|
- private boolean isMetaType(Class<?> clazz) {
|
|
|
- return DatabaseContextHolder.isTypeHandlerRegistered(clazz) || Map.class.isAssignableFrom(clazz)
|
|
|
- || Collection.class.isAssignableFrom(clazz);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 绑定字段JSON类型处理器
|
|
|
- *
|
|
|
- * @param field 表字段信息
|
|
|
- */
|
|
|
- private void bindJsonHandler(TableFieldInfo field) {
|
|
|
- Class<?> handler = this.lookupJsonHandlerClass(field.getField());
|
|
|
- ObjectUtils.setValue(field, "typeHandler", handler);
|
|
|
- String el = ObjectUtils.getValue(field, "el") + ",typeHandler=" + handler.getName();
|
|
|
- ObjectUtils.setValue(field, "el", el);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 查找JSON类型处理器对象
|
|
|
- *
|
|
|
- * @param field JSON字段实例
|
|
|
- * @return JSON类型处理器对象类型
|
|
|
- */
|
|
|
- private Class<?> lookupJsonHandlerClass(Field field) {
|
|
|
- Type type = field.getGenericType();
|
|
|
- if (ObjectUtils.isOnlyMap(type)) {
|
|
|
- return MapTypeHandler.class;
|
|
|
- } else if (ObjectUtils.isOnlySet(type)) {
|
|
|
- return SetTypeHandler.class;
|
|
|
- } else if (ObjectUtils.isOnlyList(type)) {
|
|
|
- return ListTypeHandler.class;
|
|
|
- } else if (type instanceof ParameterizedType) {
|
|
|
- Type raw = ((ParameterizedType) type).getRawType();
|
|
|
- Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
|
|
|
- if (raw == Set.class) {
|
|
|
- if (arg == int.class || arg == Integer.class) {
|
|
|
- return UniqueIntegersTypeHandler.class;
|
|
|
- } else if (arg == short.class || arg == Short.class) {
|
|
|
- return UniqueShortsTypeHandler.class;
|
|
|
- } else if (arg == long.class || arg == Long.class) {
|
|
|
- return UniqueLongsTypeHandler.class;
|
|
|
- } else if (arg == float.class || arg == Float.class) {
|
|
|
- return UniqueFloatsTypeHandler.class;
|
|
|
- } else if (arg == double.class || arg == Double.class) {
|
|
|
- return UniqueDoublesTypeHandler.class;
|
|
|
- } else if (arg == boolean.class || arg == Boolean.class) {
|
|
|
- return UniqueBooleansTypeHandler.class;
|
|
|
- } else if (arg == String.class) {
|
|
|
- return UniqueStringsTypeHandler.class;
|
|
|
- } else if (arg == File.class) {
|
|
|
- return UniqueFilesTypeHandler.class;
|
|
|
- } else if (arg == Period.class) {
|
|
|
- return UniquePeriodsTypeHandler.class;
|
|
|
- } else if (arg == Region.class) {
|
|
|
- return UniqueRegionsTypeHandler.class;
|
|
|
- } else if (arg == Modification.class) {
|
|
|
- return UniqueModificationsTypeHandler.class;
|
|
|
- }
|
|
|
- } else if (raw == List.class) {
|
|
|
- if (arg == int.class || arg == Integer.class) {
|
|
|
- return IntegersTypeHandler.class;
|
|
|
- } else if (arg == short.class || arg == Short.class) {
|
|
|
- return ShortsTypeHandler.class;
|
|
|
- } else if (arg == long.class || arg == Long.class) {
|
|
|
- return LongsTypeHandler.class;
|
|
|
- } else if (arg == float.class || arg == Float.class) {
|
|
|
- return FloatsTypeHandler.class;
|
|
|
- } else if (arg == double.class || arg == Double.class) {
|
|
|
- return DoublesTypeHandler.class;
|
|
|
- } else if (arg == boolean.class || arg == Boolean.class) {
|
|
|
- return BooleansTypeHandler.class;
|
|
|
- } else if (arg == String.class) {
|
|
|
- return StringsTypeHandler.class;
|
|
|
- } else if (arg == File.class) {
|
|
|
- return FilesTypeHandler.class;
|
|
|
- } else if (arg == Period.class) {
|
|
|
- return PeriodsTypeHandler.class;
|
|
|
- } else if (arg == Region.class) {
|
|
|
- return RegionsTypeHandler.class;
|
|
|
- } else if (arg == Modification.class) {
|
|
|
- return ModificationsTypeHandler.class;
|
|
|
- } else if (ObjectUtils.isOnlyMap(arg)) {
|
|
|
- return MapsTypeHandler.class;
|
|
|
- } else if (ObjectUtils.isOnlySet(arg)) {
|
|
|
- return SetsTypeHandler.class;
|
|
|
- } else if (ObjectUtils.isOnlyList(arg)) {
|
|
|
- return ListsTypeHandler.class;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return this.handlers.computeIfAbsent(type, t -> {
|
|
|
- try {
|
|
|
- return this.initializeJsonHandlerClass(field);
|
|
|
- } catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化JSON类型处理器对象
|
|
|
- *
|
|
|
- * @param field JSON字段实例
|
|
|
- * @return JSON类型处理器对象类型
|
|
|
- * @throws Exception 对象初始化异常
|
|
|
- */
|
|
|
- private Class<?> initializeJsonHandlerClass(Field field) throws Exception {
|
|
|
- ClassPool pool = ClassPool.getDefault();
|
|
|
- CtClass handler = pool.makeClass(String.format("_JsonTypeHandler_%d", this.counter.incrementAndGet()));
|
|
|
- handler.setSuperclass(pool.get(JsonTypeHandler.Simple.class.getName()));
|
|
|
- CtConstructor constructor = new CtConstructor(new CtClass[]{}, handler);
|
|
|
- constructor.setBody(String.format("{super(%s);}", ObjectUtils.analyse(field.getGenericType())));
|
|
|
- handler.addConstructor(constructor);
|
|
|
- return handler.toClass(this.getClass().getClassLoader(), null);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判断字段是否可以配置JSON类型处理器
|
|
|
- *
|
|
|
- * @param field 数据模型字段
|
|
|
- * @return true/false
|
|
|
- */
|
|
|
- private boolean isJsonHandlerConfigurable(Field field) {
|
|
|
- TableField annotation = field.getAnnotation(TableField.class);
|
|
|
- if (annotation == null || annotation.exist()) {
|
|
|
- Class<?> handler = ObjectUtils.ifNull(annotation, TableField::typeHandler);
|
|
|
- return (handler == null || handler == UnknownTypeHandler.class)
|
|
|
- && !DatabaseContextHolder.isTypeHandlerRegistered(field.getType());
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判断数据模型是否可以配置JSON类型处理器
|
|
|
- *
|
|
|
- * @param clazz 数据模型对象
|
|
|
- * @return true/false
|
|
|
- */
|
|
|
- private boolean isJsonHandlerConfigurable(Class<?> clazz) {
|
|
|
- if (this.isMetaType(clazz)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- Reference<Boolean> configurable = new Reference<>();
|
|
|
- ObjectUtils.iterateFields(clazz, (i, field) -> {
|
|
|
- if (this.isJsonHandlerConfigurable(field)) {
|
|
|
- configurable.set(true);
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
- });
|
|
|
- return configurable.get(false);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判断表字段是否可以配置JSON类型处理器
|
|
|
- *
|
|
|
- * @param field 表字段信息
|
|
|
- * @return true/false
|
|
|
- */
|
|
|
- private boolean isJsonHandlerConfigurable(TableFieldInfo field) {
|
|
|
- return field.getTypeHandler() == null
|
|
|
- && !DatabaseContextHolder.isTypeHandlerRegistered(field.getPropertyType());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取查询结果类型
|
|
|
- *
|
|
|
- * @param node 查询节点
|
|
|
- * @return 对象类型
|
|
|
- */
|
|
|
- private Class<?> getSelectResultType(XNode node) {
|
|
|
- String type = node.getStringAttribute("resultType");
|
|
|
- if (StringUtils.notEmpty(type)) {
|
|
|
- Class<?> clazz = this.aliases.get(type);
|
|
|
- if (clazz != null) {
|
|
|
- return clazz;
|
|
|
- }
|
|
|
- try {
|
|
|
- return Class.forName(type);
|
|
|
- } catch (ClassNotFoundException e) {
|
|
|
- log.warn("Result type class load failed: {}", e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取查询结果类型
|
|
|
- *
|
|
|
- * @param node 查询节点
|
|
|
- * @return 对象类型
|
|
|
- */
|
|
|
- private Class<?> getResultMappingType(XNode node) {
|
|
|
- String type = node.getStringAttribute("type");
|
|
|
- if (StringUtils.notEmpty(type)) {
|
|
|
- Class<?> clazz = this.aliases.get(type);
|
|
|
- if (clazz != null) {
|
|
|
- return clazz;
|
|
|
- }
|
|
|
- try {
|
|
|
- return Class.forName(type);
|
|
|
- } catch (ClassNotFoundException e) {
|
|
|
- log.warn("Result map class load failed: {}", e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化resultMap
|
|
|
- *
|
|
|
- * @param document xml文档对象
|
|
|
- * @param clazz 对象类型
|
|
|
- * @return 节点元素
|
|
|
- */
|
|
|
- private Element initializeResultMapping(Document document, Class<?> clazz) {
|
|
|
- // 构建resultMap节点元素
|
|
|
- Element element = document.createElement("resultMap");
|
|
|
- element.setAttribute("id", StringUtils.uuid());
|
|
|
- element.setAttribute("type", clazz.getName());
|
|
|
-
|
|
|
- // 添加resultMap字段映射
|
|
|
- ObjectUtils.iterateFields(clazz, (i, field) -> {
|
|
|
- String tag = field.isAnnotationPresent(TableId.class) ? "id" : "result";
|
|
|
- String column = StringUtils.ifEmpty(
|
|
|
- ObjectUtils.ifNull(field.getAnnotation(TableField.class), TableField::value),
|
|
|
- () -> StringUtils.hump2underscore(field.getName())
|
|
|
- );
|
|
|
- Element child = document.createElement(tag);
|
|
|
- child.setAttribute("column", column);
|
|
|
- child.setAttribute("property", field.getName());
|
|
|
-
|
|
|
- // 设置json类型处理器
|
|
|
- if (this.isJsonHandlerConfigurable(field)) {
|
|
|
- Class<?> handler = this.lookupJsonHandlerClass(field);
|
|
|
- child.setAttribute("typeHandler", handler.getName());
|
|
|
- }
|
|
|
- element.appendChild(child);
|
|
|
- });
|
|
|
- return element;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void intercept(XNode mapper) {
|
|
|
- // 设置已有resultMap的json类型处理器
|
|
|
- List<XNode> maps = mapper.evalNodes("resultMap");
|
|
|
- if (ObjectUtils.notEmpty(maps)) {
|
|
|
- for (XNode map : maps) {
|
|
|
- Class<?> clazz = this.getResultMappingType(map);
|
|
|
- if (clazz == null || this.isMetaType(clazz)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- for (XNode child : map.getChildren()) {
|
|
|
- // 判断当前是有存在typeHandler,如果不存在且字段满足配置json类型处理器条件,则自动绑定json类型处理器
|
|
|
- if (StringUtils.isEmpty(child.getStringAttribute("typeHandler"))) {
|
|
|
- String property = child.getStringAttribute("property");
|
|
|
- Field field = ObjectUtils.lookupField(clazz, property);
|
|
|
- if (field != null && this.isJsonHandlerConfigurable(field)) {
|
|
|
- Class<?> handler = this.lookupJsonHandlerClass(field);
|
|
|
- ((Element) child.getNode()).setAttribute("typeHandler", handler.getName());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 设置resultType对应的resultMap,并将原来的resultType替换成resultMap
|
|
|
- Document document = mapper.getNode().getOwnerDocument();
|
|
|
- List<XNode> selects = mapper.evalNodes("select");
|
|
|
- if (ObjectUtils.notEmpty(selects)) {
|
|
|
- Map<Class<?>, Element> cache = Maps.newHashMap();
|
|
|
- for (XNode select : selects) {
|
|
|
- // 判断resultType是否可配置json类型处理器
|
|
|
- Class<?> clazz = this.getSelectResultType(select);
|
|
|
- try {
|
|
|
- if (clazz != null && this.isJsonHandlerConfigurable(clazz)) {
|
|
|
- // 构建并注册resultMap
|
|
|
- Element map = cache.computeIfAbsent(clazz, c -> this.initializeResultMapping(document, clazz));
|
|
|
- mapper.getNode().appendChild(map);
|
|
|
-
|
|
|
- // 用户新生成的resultMap替换resultType
|
|
|
- Element element = (Element) select.getNode();
|
|
|
- element.removeAttribute("resultType");
|
|
|
- element.setAttribute("resultMap", map.getAttribute("id"));
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void intercept(TableInfo table) {
|
|
|
- // 自动绑定JSON类型字段处理器
|
|
|
- List<TableFieldInfo> fields = table.getFieldList();
|
|
|
- if (ObjectUtils.notEmpty(fields)) {
|
|
|
- fields.stream().filter(this::isJsonHandlerConfigurable).forEach(this::bindJsonHandler);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|