feat: add typeHandler support generic type

This commit is contained in:
开源海哥 2023-08-10 12:30:22 +08:00
parent 6c0927ffff
commit 26dc943344
5 changed files with 211 additions and 19 deletions

View File

@ -17,19 +17,39 @@ 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<Object> {
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<? extends Collection>) propertyType, genericType);
}
@Override
protected Object parseJson(String json) {
if (genericType != null && Collection.class.isAssignableFrom(propertyType)) {
return JSON.parseObject(json, type);
} else {
return JSON.parseObject(json, propertyType);
}
}
@Override
protected String toJson(Object object) {

View File

@ -18,18 +18,38 @@ 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<Object> {
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) {
if (genericType != null && Collection.class.isAssignableFrom(propertyType)) {
return JSON.parseObject(json, type);
} else {
return JSON.parseObject(json, propertyType);
}
}
@Override
protected String toJson(Object object) {
@ -37,4 +57,66 @@ public class FastjsonTypeHandler extends BaseJsonTypeHandler<Object> {
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;
}
}
}

View File

@ -16,20 +16,35 @@
package com.mybatisflex.core.handler;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
/**
* @author michael
*/
public class GsonTypeHandler extends BaseJsonTypeHandler<Object> {
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) {
if (genericType != null) {
TypeToken<?> typeToken = TypeToken.getParameterized(propertyType, genericType);
return getGson().fromJson(json, typeToken);
} else {
return getGson().fromJson(json, propertyType);
}
}
@Override
protected String toJson(Object object) {

View File

@ -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<Object> {
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 {
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<Object> {
}
}
public JavaType getJavaType() {
if (javaType == null){
javaType = getObjectMapper().getTypeFactory().constructCollectionType((Class<? extends Collection>) propertyType, genericType);
}
return javaType;
}
public static ObjectMapper getObjectMapper() {
if (null == objectMapper) {
objectMapper = new ObjectMapper();

View File

@ -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.*;
@ -310,10 +307,22 @@ public class TableInfoFactory {
columnInfo.setPropertyType(fieldType);
if (column != null && column.typeHandler() != UnknownTypeHandler.class) {
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<?> typeHandler = typeHandlerRegistry.getInstance(columnInfo.getPropertyType(), typeHandlerClass);
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();