From 26dc9433444e91795793cd43487c7688a2e42c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 10 Aug 2023 12:30:22 +0800 Subject: [PATCH] feat: add typeHandler support generic type --- .../core/handler/Fastjson2TypeHandler.java | 26 +++++- .../core/handler/FastjsonTypeHandler.java | 88 ++++++++++++++++++- .../core/handler/GsonTypeHandler.java | 21 ++++- .../core/handler/JacksonTypeHandler.java | 26 +++++- .../core/table/TableInfoFactory.java | 69 +++++++++++++-- 5 files changed, 211 insertions(+), 19 deletions(-) diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/Fastjson2TypeHandler.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/Fastjson2TypeHandler.java index b5913cbc..33831db2 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/Fastjson2TypeHandler.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/Fastjson2TypeHandler.java @@ -17,18 +17,38 @@ package com.mybatisflex.core.handler; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.TypeReference; +import java.lang.reflect.Type; +import java.util.Collection; + +/** + * @author michael + */ public class Fastjson2TypeHandler extends BaseJsonTypeHandler { private final Class propertyType; + private Class genericType; + private Type type; - public Fastjson2TypeHandler(Class type) { - this.propertyType = type; + public Fastjson2TypeHandler(Class propertyType) { + this.propertyType = propertyType; + } + + + public Fastjson2TypeHandler(Class propertyType, Class genericType) { + this.propertyType = propertyType; + this.genericType = genericType; + this.type = TypeReference.collectionType((Class) propertyType, genericType); } @Override protected Object parseJson(String json) { - return JSON.parseObject(json, propertyType); + if (genericType != null && Collection.class.isAssignableFrom(propertyType)) { + return JSON.parseObject(json, type); + } else { + return JSON.parseObject(json, propertyType); + } } @Override diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/FastjsonTypeHandler.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/FastjsonTypeHandler.java index 9edf5738..31cdb154 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/FastjsonTypeHandler.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/FastjsonTypeHandler.java @@ -18,17 +18,37 @@ package com.mybatisflex.core.handler; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collection; + +/** + * @author michael + */ public class FastjsonTypeHandler extends BaseJsonTypeHandler { private final Class propertyType; + private Class genericType; + private Type type; - public FastjsonTypeHandler(Class type) { - this.propertyType = type; + public FastjsonTypeHandler(Class propertyType) { + this.propertyType = propertyType; + } + + public FastjsonTypeHandler(Class propertyType, Class genericType) { + this.propertyType = propertyType; + this.genericType = genericType; + this.type = new ParameterizedTypeImpl(propertyType, genericType); } @Override protected Object parseJson(String json) { - return JSON.parseObject(json, propertyType); + if (genericType != null && Collection.class.isAssignableFrom(propertyType)) { + return JSON.parseObject(json, type); + } else { + return JSON.parseObject(json, propertyType); + } } @Override @@ -37,4 +57,66 @@ public class FastjsonTypeHandler extends BaseJsonTypeHandler { SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty); } + + public static class ParameterizedTypeImpl implements ParameterizedType { + + private final Type[] actualTypeArguments; + private final Type ownerType; + private final Type rawType; + + public ParameterizedTypeImpl(Type rawType, Type... actualTypeArguments) { + this.rawType = rawType; + this.actualTypeArguments = actualTypeArguments; + this.ownerType = null; + } + + @Override + public Type[] getActualTypeArguments() { + return this.actualTypeArguments; + } + + @Override + public Type getOwnerType() { + return this.ownerType; + } + + @Override + public Type getRawType() { + return this.rawType; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + ParameterizedTypeImpl that = (ParameterizedTypeImpl) o; + if (!Arrays.equals(this.actualTypeArguments, that.actualTypeArguments)) { + return false; + } else { + if (this.ownerType != null) { + if (this.ownerType.equals(that.ownerType)) { + return this.rawType != null ? this.rawType.equals(that.rawType) : that.rawType == null; + } + } else if (that.ownerType == null) { + return this.rawType != null ? this.rawType.equals(that.rawType) : that.rawType == null; + } + + return false; + } + } else { + return false; + } + } + + @Override + public int hashCode() { + int result = this.actualTypeArguments != null ? Arrays.hashCode(this.actualTypeArguments) : 0; + result = 31 * result + (this.ownerType != null ? this.ownerType.hashCode() : 0); + result = 31 * result + (this.rawType != null ? this.rawType.hashCode() : 0); + return result; + } + } + + } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/GsonTypeHandler.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/GsonTypeHandler.java index 15498380..fefe08a7 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/GsonTypeHandler.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/GsonTypeHandler.java @@ -16,19 +16,34 @@ package com.mybatisflex.core.handler; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +/** + * @author michael + */ public class GsonTypeHandler extends BaseJsonTypeHandler { private static Gson gson; private final Class propertyType; + private Class genericType; - public GsonTypeHandler(Class type) { - this.propertyType = type; + public GsonTypeHandler(Class propertyType) { + this.propertyType = propertyType; + } + + public GsonTypeHandler(Class propertyType, Class genericType) { + this.propertyType = propertyType; + this.genericType = genericType; } @Override protected Object parseJson(String json) { - return getGson().fromJson(json, propertyType); + if (genericType != null) { + TypeToken typeToken = TypeToken.getParameterized(propertyType, genericType); + return getGson().fromJson(json, typeToken); + } else { + return getGson().fromJson(json, propertyType); + } } @Override diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/JacksonTypeHandler.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/JacksonTypeHandler.java index 7984387f..57ab21e4 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/JacksonTypeHandler.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/handler/JacksonTypeHandler.java @@ -16,24 +16,40 @@ package com.mybatisflex.core.handler; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.mybatisflex.core.exception.FlexExceptions; import java.io.IOException; +import java.util.Collection; +/** + * @author michael + */ public class JacksonTypeHandler extends BaseJsonTypeHandler { private static ObjectMapper objectMapper; private final Class propertyType; + private Class genericType; + private JavaType javaType; public JacksonTypeHandler(Class propertyType) { this.propertyType = propertyType; } + public JacksonTypeHandler(Class propertyType, Class genericType) { + this.propertyType = propertyType; + this.genericType = genericType; + } + @Override protected Object parseJson(String json) { try { - return getObjectMapper().readValue(json, propertyType); + if (genericType != null && Collection.class.isAssignableFrom(propertyType)) { + return getObjectMapper().readValue(json, getJavaType()); + } else { + return getObjectMapper().readValue(json, propertyType); + } } catch (IOException e) { throw FlexExceptions.wrap(e, "Can not parseJson by JacksonTypeHandler: " + json); } @@ -48,6 +64,14 @@ public class JacksonTypeHandler extends BaseJsonTypeHandler { } } + + public JavaType getJavaType() { + if (javaType == null){ + javaType = getObjectMapper().getTypeFactory().constructCollectionType((Class) propertyType, genericType); + } + return javaType; + } + public static ObjectMapper getObjectMapper() { if (null == objectMapper) { objectMapper = new ObjectMapper(); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java index cc086083..0ef4a0e2 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java @@ -27,10 +27,7 @@ import org.apache.ibatis.io.ResolverUtil; import org.apache.ibatis.reflection.Reflector; import org.apache.ibatis.reflection.TypeParameterResolver; import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.type.JdbcType; -import org.apache.ibatis.type.TypeHandler; -import org.apache.ibatis.type.TypeHandlerRegistry; -import org.apache.ibatis.type.UnknownTypeHandler; +import org.apache.ibatis.type.*; import org.apache.ibatis.util.MapUtil; import java.lang.reflect.*; @@ -218,7 +215,7 @@ public class TableInfoFactory { Type genericType = TypeParameterResolver.resolveFieldType(field, entityClass); if (genericType instanceof ParameterizedType) { Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - if (actualTypeArgument instanceof Class){ + if (actualTypeArgument instanceof Class) { tableInfo.addCollectionType(field, (Class) actualTypeArgument); } } @@ -310,10 +307,22 @@ public class TableInfoFactory { columnInfo.setPropertyType(fieldType); if (column != null && column.typeHandler() != UnknownTypeHandler.class) { - Class typeHandlerClass = column.typeHandler(); - Configuration configuration = FlexGlobalConfig.getDefaultConfig().getConfiguration(); - TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); - TypeHandler typeHandler = typeHandlerRegistry.getInstance(columnInfo.getPropertyType(), typeHandlerClass); + TypeHandler typeHandler; + + // 集合类型,传入泛型 + // fixed https://gitee.com/mybatis-flex/mybatis-flex/issues/I7S2YE + if (Collection.class.isAssignableFrom(fieldType)) { + typeHandler = createCollectionTypeHandler(entityClass, field, column.typeHandler(), fieldType); + } + + //非集合类型 + else { + Class typeHandlerClass = column.typeHandler(); + Configuration configuration = FlexGlobalConfig.getDefaultConfig().getConfiguration(); + TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); + typeHandler = typeHandlerRegistry.getInstance(columnInfo.getPropertyType(), typeHandlerClass); + } + columnInfo.setTypeHandler(typeHandler); } @@ -359,6 +368,48 @@ public class TableInfoFactory { return tableInfo; } + /** + * 创建 typeHandler + * 参考 {@link TypeHandlerRegistry#getInstance(Class, Class)} + * + * @param entityClass + * @param field + * @param typeHandlerClass + * @param fieldType + */ + private static TypeHandler createCollectionTypeHandler(Class entityClass, Field field, Class typeHandlerClass, Class fieldType) { + Class genericClass = null; + Type genericType = TypeParameterResolver.resolveFieldType(field, entityClass); + if (genericType instanceof ParameterizedType) { + Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0]; + if (actualTypeArgument instanceof Class) { + genericClass = (Class) actualTypeArgument; + } + } + + try { + Constructor constructor = typeHandlerClass.getConstructor(Class.class, Class.class); + return (TypeHandler) constructor.newInstance(fieldType, genericClass); + } catch (NoSuchMethodException ignored) { + } catch (Exception e) { + throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e); + } + try { + Constructor constructor = typeHandlerClass.getConstructor(Class.class); + return (TypeHandler) constructor.newInstance(fieldType); + } catch (NoSuchMethodException ignored) { + } catch (Exception e) { + throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e); + } + try { + Constructor c = typeHandlerClass.getConstructor(); + return (TypeHandler) c.newInstance(); + } catch (Exception e) { + throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e); + } + } + + static String getColumnName(boolean isCamelToUnderline, Field field, Column column) { if (column != null && StringUtil.isNotBlank(column.value())) { return column.value();