通过映射将MAP数据映射到对应的实体类
- 通过映射来给实体赋值,new一个对应实体类的class实例,然后取到实体类的所有属性数组,遍历属性数组,从map中获取对应的value值。注意:获取的属性数组中只能获取到它当前的所有属性,不能获取到它继承的属性,想要获取到其父类的属性可以通过获取到其父类字节码,然后在获取父类中的所有属性;
有一个问题是:获取value值时,是根据当前类属性的字段名称来获取它的value值,所以必须要求类属性的名称与map中的key值完全一致,否则不能获取到值。具体代码如下:
//这里clazz参数代表的是需要转换的对应实体类class,map就是要取值的map,我们调用的时候可以直接传map和实体class。如:
mapTObject(map, A.class);
public static Object mapTObject(Map<String, Object> map,Class<?> clazz){
if(map == null){
return null;
}
Object obj = null;
try {
obj = clazz.newInstance();
Field[] fields = obj.getClass().getDeclaredFields();//获取到所有属性,不包括继承的属性
Field[] supFields = obj.getClass().getSuperclass().getDeclaredFields();//获取传入类的父类的所有属性
for(Field field : fields){
int mod = field.getModifiers();//获取字段的修饰符
if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
continue;
}
field.setAccessible(true);
field.set(obj, map.get(field.getName()));//根据属性名称去map获取value
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
通过实体挨个赋值
直接从map中根据key获取value值来赋值给对应实体类的属性。缺点是会比较麻烦一些,需要挨个给获取map中额key值赋值给实体。如下:
User user = User.builder()
.name(StringUtils.isNotBlank(map.get("name")) ? map.get("name") : null)
.age(map.get("age"))
.build();
自定义转译工具类,转译实体类
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.vladmihalcea.hibernate.type.json.internal.JacksonUtil;
import lombok.*;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.ResultTransformer;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ConcurrentReferenceHashMap;
import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
@Autowired
private static EntityManager entityManager;
public static void main(String[] args) {
//自定义SQL查询语句
String sql = "select * from user ";
int nowPage = 1;
int pageSize = 10;
Query pageQuery = entityManager.createNativeQuery(sql);
pageQuery.setFirstResult((nowPage-1) * pageSize);
pageQuery.setMaxResults(pageSize);
NativeQueryImplementor<User> implementor = pageQuery.unwrap(NativeQueryImpl.class).setResultTransformer(new BaseTransformer(User.class));
List<User> resultList = implementor.list();
}
}
class BaseTransformer implements ResultTransformer {
/**
* 缓存
*/
private static Map<Class, Map<String, DOProperties>> cacheMap = new ConcurrentReferenceHashMap<>();
/**
* 列名-属性名,属性类型
*/
private Map<String, DOProperties> map;
private Class resultClass;
private static Pattern CAMEL_PATTERN = Pattern.compile("([a-z])([A-Z])");
private static final String UNDER_LINE = "_";
private static final int DATE = 1;
private static final int DATE_TIME = 2;
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
private static Set<Class> DATE_CLASS = Sets.newHashSet(java.sql.Date.class,
LocalDateTime.class, LocalDate.class, Timestamp.class);
private static Set<Class> GEOMETRY_CLASS = Sets.newHashSet(Geometry.class);
private static Set<Class> JSON_CLASS = Sets.newHashSet(JsonNode.class, JSONObject.class, JSONArray.class);
private static List<Set<Class>> CLASS_LIST = Lists.newArrayList(DATE_CLASS, GEOMETRY_CLASS, JSON_CLASS);
@SneakyThrows
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Object result = resultClass.newInstance();
for (int i = 0; i < tuple.length; i++) {
if (tuple[i] == null) {
continue;
}
if (map.containsKey(aliases[i])) {
//属性名-属性类型
DOProperties property = map.get(aliases[i]);
if (property != null) {
Object value;
Class type = property.getClassType();
if (support(tuple[i].getClass(), type)) {
value = transferType(tuple[i], type);
} else {
value = tuple[i];
}
//TODO 性能有问题,修改实现
BeanUtils.copyProperty(result, property.getClassName(), value);
// property.getSetter().invoke(result, value);
}
}
}
return result;
}
@Override
public List transformList(List list) {
return list;
}
/**
* TODO 通过工厂类获取单例对象
*/
public BaseTransformer (Class clazz) {
this.resultClass = clazz;
map = cacheMap.get(resultClass);
if (map == null) {
map = new HashMap<>(16);
//缓存实体类属性与数据库列名的对应关系?
Class curr = resultClass;
while (curr != null && curr != Object.class) {
for (Field field : curr.getDeclaredFields()) {
Column annotation = field.getAnnotation(Column.class);
String columnName;
//如过属性有column注解,则使用对应的列名,否则默认使用驼峰转下划线
if (annotation != null && StringUtils.isNotBlank(annotation.name())) {
columnName = annotation.name();
} else {
columnName = camelToUnderline(field.getName());
}
DOProperties dtoProperties = DOProperties.builder()
.column(columnName)
.className(field.getName())
.classType(field.getType())
.setter(getSetter(resultClass, field))
.build();
map.put(columnName, dtoProperties);
//代码中已经存在一些注解使用了大写的字段名,但是表的字段名是小写的情况,处理一下
String lowerColumnName = columnName.toLowerCase();
if (!map.containsKey(lowerColumnName)) {
map.put(lowerColumnName, dtoProperties);
}
}
curr = curr.getSuperclass();
}
cacheMap.put(resultClass, map);
}
}
private Method getSetter(Class resultClass, Field field) {
String fieldName = field.getName();
Class fieldType = field.getType();
String setter = "set" + Character.toUpperCase(fieldName.charAt(0)) +
(fieldName.length() > 1 ? fieldName.substring(1) : "");
try {
return resultClass.getMethod(setter, fieldType);
} catch (NoSuchMethodException e) {
return null;
}
}
public static String camelToUnderline(String name) {
Matcher matcher = CAMEL_PATTERN.matcher(name);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, matcher.group(1) + UNDER_LINE + matcher.group(2).toLowerCase());
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* 判断是否支持类型转换
* @param source
* @param target
* @return
*/
public static boolean support(Class source, Class target) {
if (source == target) {
return true;
}
if (source == String.class) {
for (Set<Class> classSet : CLASS_LIST) {
if (classSet.contains(target)) {
return true;
}
}
return false;
} else {
for (Set<Class> classSet : CLASS_LIST) {
//实现了同种分类格式之间的转换(如不同的时间格式之间转换)
if (classSet.contains(source)) {
return target == String.class || classSet.contains(target);
}
}
return false;
}
}
/**
* 类型转换
*
* @param parameter 原始值
* @param targetType 目标类型
* @param <T>
* @return
* @see #support(Class, Class) 实现了该方法返回true的类型转换
*/
@SneakyThrows
public static <T> T transferType(Object parameter, Class<T> targetType) {
if (parameter == null) {
return null;
}
if (parameter.getClass() == targetType) {
return (T) parameter;
}
//将原始参数类型统一转换成String,便于向目标类型转换。减少需要实现的代码,不过会降低些转换效率
String str = formatToStr(parameter);
if (targetType == String.class) {
return (T) str;
} else if (targetType == Geometry.class) {
WKTReader reader = new WKTReader();
return (T) reader.read(parameter.toString());
} else if (targetType == LocalDate.class) {
return (T) LocalDate.parse(formatDateStr(str, DATE), DATE_FORMATTER);
} else if (targetType == LocalDateTime.class) {
return (T) LocalDateTime.parse(formatDateStr(str, DATE_TIME), DATE_TIME_FORMATTER);
} else if (targetType == Timestamp.class) {
LocalDateTime localDateTime = LocalDateTime.parse(formatDateStr(str, DATE), DATE_TIME_FORMATTER);
return (T) Timestamp.valueOf(localDateTime);
} else if (targetType == java.sql.Date.class) {
LocalDate localDate = LocalDate.parse(formatDateStr(str, DATE), DATE_FORMATTER);
return (T) java.sql.Date.valueOf(localDate);
} else if (targetType == Integer.class) {
return (T) Integer.valueOf(str);
} else if (targetType == Long.class) {
return (T) Long.valueOf(str);
} else if (targetType == Short.class) {
return (T) Short.valueOf(str);
} else if (targetType == Boolean.class) {
return (T) Boolean.valueOf(str);
} else if (targetType == Float.class) {
return (T) Float.valueOf(str);
} else if (targetType == Double.class) {
return (T) Double.valueOf(str);
} else if (targetType == Byte.class) {
return (T) Byte.valueOf(str);
} else if (targetType == JsonNode.class){
return (T) JacksonUtil.toJsonNode(str);
} else if (targetType == JSONObject.class) {
return (T) JSONObject.parseObject(str);
} else if (targetType == JSONArray.class) {
return (T) JSONObject.parseArray(str);
} else {
throw new IllegalArgumentException("不支持的目标类型:" + targetType.getName());
}
}
/**
* 转换两个日期字符串的格式
*
* @param str
* @param date
* @return
*/
private static CharSequence formatDateStr(String str, int date) {
if (str.length() == DATE_FORMAT.length()) {
//将只有日期的格式转换为日期和时间的格式
if (date == DATE_TIME) {
str = str + " 00:00:00";
}
} else if (str.length() == DATE_TIME_FORMAT.length()) {
//将日期和时间的格式转换为只有日期的格式
if (date == DATE) {
str = str.substring(0, DATE_FORMAT.length());
}
}
return str;
}
private static String formatToStr(Object v) {
//先将日期类型转换成统一格式的字符串
if (v instanceof Timestamp) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
return simpleDateFormat.format((Timestamp) v);
} else if (v instanceof LocalDateTime) {
return DATE_TIME_FORMATTER.format((LocalDateTime) v);
} else if (v instanceof LocalDate) {
return DATE_FORMATTER.format((LocalDate) v);
} else if (v instanceof java.sql.Date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
return simpleDateFormat.format((java.sql.Date) v);
} else {
return v.toString();
}
}
}
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DOProperties {
private String column;
private Class classType;
private String className;
private Method setter;
}