|
@@ -1,4 +1,4 @@
|
|
|
-package com.chelvc.framework.database.support;
|
|
|
+package com.chelvc.framework.database.interceptor;
|
|
|
|
|
|
import java.lang.reflect.Field;
|
|
|
import java.lang.reflect.ParameterizedType;
|
|
@@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
|
|
import com.baomidou.mybatisplus.annotation.TableField;
|
|
|
import com.baomidou.mybatisplus.annotation.TableId;
|
|
|
-import com.baomidou.mybatisplus.core.config.GlobalConfig;
|
|
|
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
|
|
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
|
|
import com.chelvc.framework.common.model.File;
|
|
@@ -42,118 +41,35 @@ import com.google.common.collect.Maps;
|
|
|
import javassist.ClassPool;
|
|
|
import javassist.CtClass;
|
|
|
import javassist.CtConstructor;
|
|
|
-import javassist.CtMethod;
|
|
|
-import javassist.CtNewMethod;
|
|
|
-import javassist.NotFoundException;
|
|
|
-import lombok.NonNull;
|
|
|
import org.apache.ibatis.parsing.XNode;
|
|
|
import org.apache.ibatis.type.UnknownTypeHandler;
|
|
|
import org.w3c.dom.Document;
|
|
|
import org.w3c.dom.Element;
|
|
|
|
|
|
/**
|
|
|
- * JSON类型绑定处理工具类
|
|
|
+ * JSON类型字段处理器配置拦截器
|
|
|
*
|
|
|
* @author Woody
|
|
|
- * @date 2024/4/5
|
|
|
+ * @date 2024/4/6
|
|
|
*/
|
|
|
-public final class JsonTypeHandlerBinder {
|
|
|
+public class JsonHandlerConfigureInterceptor extends MybatisConfigureInterceptor {
|
|
|
/**
|
|
|
* JSON类型处理器计数器
|
|
|
*/
|
|
|
- private static final AtomicLong JSON_HANDLER_COUNTER = new AtomicLong(0);
|
|
|
+ private final AtomicLong counter = new AtomicLong(0);
|
|
|
|
|
|
/**
|
|
|
* JSON类型/处理器映射表
|
|
|
*/
|
|
|
- private static final Map<Type, Class<?>> JSON_HANDLER_CLASSES = Maps.newConcurrentMap();
|
|
|
-
|
|
|
- private JsonTypeHandlerBinder() {
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化JSON类型处理器绑定工具
|
|
|
- */
|
|
|
- public static void initialize() {
|
|
|
- try {
|
|
|
- initializeMapperListener();
|
|
|
- initializeTableInfoListener();
|
|
|
- } catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化Mapper解析监听器
|
|
|
- *
|
|
|
- * @throws Exception 初始化异常
|
|
|
- */
|
|
|
- private static void initializeMapperListener() throws Exception {
|
|
|
- ClassPool pool = ClassPool.getDefault();
|
|
|
-
|
|
|
- // 替换XMLMapperBuilder.parse方法逻辑,加入Mapper解析监听回调逻辑
|
|
|
- CtClass clazz = pool.get("org.apache.ibatis.builder.xml.XMLMapperBuilder");
|
|
|
- CtMethod method = clazz.getDeclaredMethod("parse");
|
|
|
- method.setBody(String.format("{\n" +
|
|
|
- "if (!this.configuration.isResourceLoaded(this.resource)) {\n" +
|
|
|
- " %s node = this.parser.evalNode(\"/mapper\");\n" +
|
|
|
- " %s.configure(node);\n" +
|
|
|
- " this.configurationElement(node);\n" +
|
|
|
- " this.configuration.addLoadedResource(this.resource);\n" +
|
|
|
- " this.bindMapperForNamespace();\n" +
|
|
|
- "}\n" +
|
|
|
- "this.parsePendingResultMaps();\n" +
|
|
|
- "this.parsePendingCacheRefs();\n" +
|
|
|
- "this.parsePendingStatements();\n" +
|
|
|
- "}", XNode.class.getName(), JsonTypeHandlerBinder.class.getName()));
|
|
|
- clazz.toClass();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 初始化数据模型表信息监听器
|
|
|
- *
|
|
|
- * @throws Exception 初始化异常
|
|
|
- */
|
|
|
- private static void initializeTableInfoListener() throws Exception {
|
|
|
- ClassPool pool = ClassPool.getDefault();
|
|
|
-
|
|
|
- // 获取原始initTableFields方法
|
|
|
- CtMethod initTableFields = null;
|
|
|
- CtClass clazz = pool.get("com.baomidou.mybatisplus.core.metadata.TableInfoHelper");
|
|
|
- for (CtMethod method : clazz.getDeclaredMethods()) {
|
|
|
- if (method.getName().equals("initTableFields")) {
|
|
|
- initTableFields = method;
|
|
|
- }
|
|
|
- }
|
|
|
- if (initTableFields == null) {
|
|
|
- throw new NotFoundException("initTableFields");
|
|
|
- }
|
|
|
-
|
|
|
- // 重命名initTableFields方法名
|
|
|
- CtMethod copy = CtNewMethod.copy(initTableFields, clazz, null);
|
|
|
- copy.setName("doInitTableFields");
|
|
|
- clazz.removeMethod(initTableFields);
|
|
|
- clazz.addMethod(copy);
|
|
|
-
|
|
|
- // 替换initTableFields方法,加入表信息初始化回调逻辑
|
|
|
- CtMethod replace = CtNewMethod.make(String.format("private static void initTableFields(" +
|
|
|
- "Class clazz, %s globalConfig, %s tableInfo, java.util.List excludeProperty) {\n" +
|
|
|
- "doInitTableFields(clazz, globalConfig, tableInfo, excludeProperty);\n" +
|
|
|
- "%s.configure(tableInfo);\n" +
|
|
|
- "}",
|
|
|
- GlobalConfig.class.getName(), TableInfo.class.getName(), JsonTypeHandlerBinder.class.getName()), clazz
|
|
|
- );
|
|
|
- clazz.addMethod(replace);
|
|
|
- clazz.toClass();
|
|
|
- }
|
|
|
+ private final Map<Type, Class<?>> handlers = Maps.newConcurrentMap();
|
|
|
|
|
|
/**
|
|
|
* 绑定字段JSON类型处理器
|
|
|
*
|
|
|
* @param field 表字段信息
|
|
|
*/
|
|
|
- private static void bindJsonHandler(TableFieldInfo field) {
|
|
|
- Class<?> handler = lookupJsonHandlerClass(field.getField());
|
|
|
+ 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);
|
|
@@ -165,7 +81,7 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @param field JSON字段实例
|
|
|
* @return JSON类型处理器对象类型
|
|
|
*/
|
|
|
- private static Class<?> lookupJsonHandlerClass(Field field) {
|
|
|
+ private Class<?> lookupJsonHandlerClass(Field field) {
|
|
|
Type type = field.getGenericType();
|
|
|
if (ObjectUtils.isOnlyMap(type)) {
|
|
|
return MapTypeHandler.class;
|
|
@@ -205,9 +121,9 @@ public final class JsonTypeHandlerBinder {
|
|
|
return ListsTypeHandler.class;
|
|
|
}
|
|
|
}
|
|
|
- return JSON_HANDLER_CLASSES.computeIfAbsent(type, t -> {
|
|
|
+ return this.handlers.computeIfAbsent(type, t -> {
|
|
|
try {
|
|
|
- return initializeJsonHandlerClass(field);
|
|
|
+ return this.initializeJsonHandlerClass(field);
|
|
|
} catch (Exception e) {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
@@ -222,12 +138,11 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @return JSON类型处理器对象类型
|
|
|
* @throws Exception 对象初始化异常
|
|
|
*/
|
|
|
- private static Class<?> initializeJsonHandlerClass(Field field) throws Exception {
|
|
|
+ private Class<?> initializeJsonHandlerClass(Field field) throws Exception {
|
|
|
ClassPool pool = ClassPool.getDefault();
|
|
|
CtClass handler = pool.makeClass(String.format(
|
|
|
"%s.JsonTypeHandler_%d",
|
|
|
- field.getDeclaringClass().getPackage().getName(),
|
|
|
- JSON_HANDLER_COUNTER.incrementAndGet()
|
|
|
+ field.getDeclaringClass().getPackage().getName(), this.counter.incrementAndGet()
|
|
|
));
|
|
|
handler.setSuperclass(pool.get(JsonTypeHandler.Simple.class.getName()));
|
|
|
CtConstructor constructor = new CtConstructor(new CtClass[]{}, handler);
|
|
@@ -242,7 +157,7 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @param field 数据模型字段
|
|
|
* @return true/false
|
|
|
*/
|
|
|
- private static boolean isJsonHandlerConfigurable(Field field) {
|
|
|
+ private boolean isJsonHandlerConfigurable(Field field) {
|
|
|
TableField annotation = field.getAnnotation(TableField.class);
|
|
|
if (annotation == null || annotation.exist()) {
|
|
|
Class<?> handler = ObjectUtils.ifNull(annotation, TableField::typeHandler);
|
|
@@ -258,10 +173,10 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @param clazz 数据模型对象
|
|
|
* @return true/false
|
|
|
*/
|
|
|
- private static boolean isJsonHandlerConfigurable(Class<?> clazz) {
|
|
|
+ private boolean isJsonHandlerConfigurable(Class<?> clazz) {
|
|
|
Reference<Boolean> configurable = new Reference<>();
|
|
|
ObjectUtils.iterateFields(clazz, (i, field) -> {
|
|
|
- if (isJsonHandlerConfigurable(field)) {
|
|
|
+ if (this.isJsonHandlerConfigurable(field)) {
|
|
|
configurable.set(true);
|
|
|
return false;
|
|
|
}
|
|
@@ -276,7 +191,7 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @param field 表字段信息
|
|
|
* @return true/false
|
|
|
*/
|
|
|
- private static boolean isJsonHandlerConfigurable(TableFieldInfo field) {
|
|
|
+ private boolean isJsonHandlerConfigurable(TableFieldInfo field) {
|
|
|
return field.getTypeHandler() == null
|
|
|
&& !DatabaseContextHolder.isTypeHandlerRegistered(field.getPropertyType());
|
|
|
}
|
|
@@ -287,7 +202,7 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @param node 查询节点
|
|
|
* @return 对象类型
|
|
|
*/
|
|
|
- private static Class<?> getSelectResultType(XNode node) {
|
|
|
+ private Class<?> getSelectResultType(XNode node) {
|
|
|
String type = node.getStringAttribute("resultType");
|
|
|
if (StringUtils.isEmpty(type)) {
|
|
|
return null;
|
|
@@ -305,7 +220,7 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @param node 查询节点
|
|
|
* @return 对象类型
|
|
|
*/
|
|
|
- private static Class<?> getResultMappingType(XNode node) {
|
|
|
+ private Class<?> getResultMappingType(XNode node) {
|
|
|
try {
|
|
|
return Class.forName(node.getStringAttribute("type"));
|
|
|
} catch (ClassNotFoundException e) {
|
|
@@ -320,11 +235,10 @@ public final class JsonTypeHandlerBinder {
|
|
|
* @param clazz 对象类型
|
|
|
* @return 节点元素
|
|
|
*/
|
|
|
- private static Element initializeResultMapping(Document document, Class<?> clazz) {
|
|
|
+ private Element initializeResultMapping(Document document, Class<?> clazz) {
|
|
|
// 构建resultMap节点元素
|
|
|
Element element = document.createElement("resultMap");
|
|
|
- String id = "__" + StringUtils.hump2underscore(clazz.getSimpleName()).toUpperCase() + "_RESULT_MAP";
|
|
|
- element.setAttribute("id", id);
|
|
|
+ element.setAttribute("id", StringUtils.uuid());
|
|
|
element.setAttribute("type", clazz.getName());
|
|
|
|
|
|
// 添加resultMap字段映射
|
|
@@ -339,8 +253,8 @@ public final class JsonTypeHandlerBinder {
|
|
|
child.setAttribute("property", field.getName());
|
|
|
|
|
|
// 设置json类型处理器
|
|
|
- if (isJsonHandlerConfigurable(field)) {
|
|
|
- Class<?> handler = lookupJsonHandlerClass(field);
|
|
|
+ if (this.isJsonHandlerConfigurable(field)) {
|
|
|
+ Class<?> handler = this.lookupJsonHandlerClass(field);
|
|
|
child.setAttribute("typeHandler", handler.getName());
|
|
|
}
|
|
|
element.appendChild(child);
|
|
@@ -348,24 +262,20 @@ public final class JsonTypeHandlerBinder {
|
|
|
return element;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 配置Mapper XML节点
|
|
|
- *
|
|
|
- * @param mapper Mapper XML节点
|
|
|
- */
|
|
|
- public static void configure(@NonNull XNode mapper) {
|
|
|
+ @Override
|
|
|
+ public void intercept(XNode mapper) {
|
|
|
// 设置已有resultMap的json类型处理器
|
|
|
List<XNode> maps = mapper.evalNodes("resultMap");
|
|
|
if (ObjectUtils.notEmpty(maps)) {
|
|
|
maps.forEach(map -> {
|
|
|
- Class<?> clazz = getResultMappingType(map);
|
|
|
+ Class<?> clazz = this.getResultMappingType(map);
|
|
|
for (XNode child : map.getChildren()) {
|
|
|
// 判断当前是有存在typeHandler,如果不存在且字段满足配置json类型处理器条件,则自动绑定json类型处理器
|
|
|
if (StringUtils.isEmpty(child.getStringAttribute("typeHandler"))) {
|
|
|
String property = child.getStringAttribute("property");
|
|
|
Field field = ObjectUtils.getField(clazz, property);
|
|
|
- if (isJsonHandlerConfigurable(field)) {
|
|
|
- Class<?> handler = lookupJsonHandlerClass(field);
|
|
|
+ if (this.isJsonHandlerConfigurable(field)) {
|
|
|
+ Class<?> handler = this.lookupJsonHandlerClass(field);
|
|
|
((Element) child.getNode()).setAttribute("typeHandler", handler.getName());
|
|
|
}
|
|
|
}
|
|
@@ -380,10 +290,10 @@ public final class JsonTypeHandlerBinder {
|
|
|
Map<Class<?>, Element> cache = Maps.newHashMap();
|
|
|
selects.forEach(select -> {
|
|
|
// 判断resultType是否可配置json类型处理器
|
|
|
- Class<?> clazz = getSelectResultType(select);
|
|
|
- if (clazz != null && isJsonHandlerConfigurable(clazz)) {
|
|
|
+ Class<?> clazz = this.getSelectResultType(select);
|
|
|
+ if (clazz != null && this.isJsonHandlerConfigurable(clazz)) {
|
|
|
// 构建并注册resultMap
|
|
|
- Element map = cache.computeIfAbsent(clazz, c -> initializeResultMapping(document, clazz));
|
|
|
+ Element map = cache.computeIfAbsent(clazz, c -> this.initializeResultMapping(document, clazz));
|
|
|
mapper.getNode().appendChild(map);
|
|
|
|
|
|
// 用户新生成的resultMap替换resultType
|
|
@@ -395,17 +305,12 @@ public final class JsonTypeHandlerBinder {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 配置数据模型表信息
|
|
|
- *
|
|
|
- * @param table 数据模型表信息
|
|
|
- */
|
|
|
- public static void configure(@NonNull TableInfo table) {
|
|
|
+ @Override
|
|
|
+ public void intercept(TableInfo table) {
|
|
|
// 自动绑定JSON类型字段处理器
|
|
|
List<TableFieldInfo> fields = table.getFieldList();
|
|
|
if (ObjectUtils.notEmpty(fields)) {
|
|
|
- fields.stream().filter(JsonTypeHandlerBinder::isJsonHandlerConfigurable)
|
|
|
- .forEach(JsonTypeHandlerBinder::bindJsonHandler);
|
|
|
+ fields.stream().filter(this::isJsonHandlerConfigurable).forEach(this::bindJsonHandler);
|
|
|
}
|
|
|
}
|
|
|
}
|