使用SpringDataJdbc的@Query註解實現自動映射結果集 ----- RowMapper接口

使用@Query註解的時候,常常需要寫sql來映射非域類的實例,通常的做法就是 實現 RowMapper接口,然後new實例一個一個的設置值進去。。。爲此。出世了自動映射工具類

 

注意事項:此抽象類只是結果集 一條記錄到行  的映射,添加了額外的方法;比如就是把一個字段的值拆分成Map或List,然後會自動組裝成類實例

 

 

這是抽象類,也是主要的,你的域類繼承這個抽象類即可,無需任何實現,@Query(....,rowMapperClass=域類.class) 即可

package cn.dmahz.dao.repo.model;

import cn.dmahz.dao.repo.model.article.ArticleInfoRepoModel;
import cn.dmahz.utils.MyReflectUtils;
import com.sun.rowset.JdbcRowSetImpl;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.ConvertUtils;
import org.springframework.jdbc.core.RowMapper;

import java.lang.reflect.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 自動映射 RowMapping
 * @author Dream
 */
@Slf4j
public abstract class RowMapperAutoMapping<T> implements RowMapper<T> {

    private static final Set<Class<?>> BASE_TYPE_SET = new HashSet<>();
    private static final Set<Class<?>> ARRAY_BASE_TYPE_SET = new HashSet<>();

    static {
        BASE_TYPE_SET.add(String.class);
        BASE_TYPE_SET.add(Byte.class);
        BASE_TYPE_SET.add(Byte.TYPE);
        BASE_TYPE_SET.add(Double.class);
        BASE_TYPE_SET.add(Double.TYPE);
        BASE_TYPE_SET.add(Float.class);
        BASE_TYPE_SET.add(Float.TYPE);
        BASE_TYPE_SET.add(Integer.class);
        BASE_TYPE_SET.add(Integer.TYPE);
        BASE_TYPE_SET.add(Long.class);
        BASE_TYPE_SET.add(Long.TYPE);
        BASE_TYPE_SET.add(Short.class);
        BASE_TYPE_SET.add(Short.TYPE);
        BASE_TYPE_SET.add(Boolean.class);
        BASE_TYPE_SET.add(Boolean.TYPE);

        ARRAY_BASE_TYPE_SET.add(String[].class);
        ARRAY_BASE_TYPE_SET.add(Byte[].class);
        ARRAY_BASE_TYPE_SET.add(byte[].class);
        ARRAY_BASE_TYPE_SET.add(Double[].class);
        ARRAY_BASE_TYPE_SET.add(double[].class);
        ARRAY_BASE_TYPE_SET.add(Float[].class);
        ARRAY_BASE_TYPE_SET.add(float[].class);
        ARRAY_BASE_TYPE_SET.add(Integer[].class);
        ARRAY_BASE_TYPE_SET.add(int[].class);
        ARRAY_BASE_TYPE_SET.add(Long[].class);
        ARRAY_BASE_TYPE_SET.add(long[].class);
        ARRAY_BASE_TYPE_SET.add(Short[].class);
        ARRAY_BASE_TYPE_SET.add(short[].class);
        ARRAY_BASE_TYPE_SET.add(Boolean[].class);
        ARRAY_BASE_TYPE_SET.add(boolean[].class);
    }

    private static final Pattern COMPILE = Pattern.compile("([A-Z])+");

    /**
     * 解析處理值,將值解析爲數組形式並返回
     * @param fieldName
     * @param value
     */
    protected List<Object> parsedValueIsArray(String fieldName,Object value){
        return new ArrayList<>();
    }

    /**
     * 將字段值轉換成 List Map對象,其中List中的每個Map元素對應一個類實例
     * @param fieldName
     * @param value
     * @return
     */
    protected List<Map<String,Object>> parsedValueIsArrayMap(String fieldName,Object value){
        return new ArrayList<>();
    }

    public T autoMapping(ResultSet rs,Class<T> tClass) throws SQLException, IllegalAccessException, InstantiationException {
        T t = tClass.newInstance();
        Field[] allFields = MyReflectUtils.getAllFields(tClass);
        for (Field field : allFields) {

            int modifiers = field.getModifiers();
            if(Modifier.isStatic(modifiers) ||  Modifier.isFinal(modifiers)){
                continue;
            }

            switchIf(t,field,rs,null);
        }
        return t;
    }

    /**
     * 獲取集合的實例
     * @return
     */
    protected Collection<?> getCollectionInstance(){
        return new ArrayList<>();
    }

    @SuppressWarnings("DuplicatedCode")
    private void switchIf(Object o, Field field, ResultSet rs, Map<String,Object> valueMap) throws IllegalAccessException, SQLException, InstantiationException {
        Object extractValue = extractValue(rs, valueMap, field.getName());
        Type genericType = field.getGenericType();
        if(genericType instanceof ParameterizedType){
            // 泛型類型
            ParameterizedType parameterizedType = (ParameterizedType)genericType;
            Type rawType = parameterizedType.getRawType();
            if(Collection.class.isAssignableFrom((Class<?>) rawType)){
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                if(actualTypeArguments.length > 1){
                    throw new RuntimeException("僅支持單泛型Copy");
                }
                Class<?> argumentCls = (Class<?>) actualTypeArguments[0];

                boolean b = copyMappingField(o, field, argumentCls, extractValue);
                if(!b){
                    List<Map<String, Object>> maps = parsedValueIsArrayMap(field.getName(), extractValue);
                    Object instance;
                    if(!((Class<?>) rawType).isInterface()){
                        instance = ((Class<?>) rawType).newInstance();
                    }else{
                        instance = getCollectionInstance();
                    }

                    @SuppressWarnings("unchecked") Collection<Object> coll = (Collection<Object>) instance;
                    Object argInstance;
                    for (Map<String, Object> map : maps) {
                        argInstance = argumentCls.newInstance();
                        Field[] allFields = MyReflectUtils.getAllFields(argumentCls);
                        for (Field f : allFields) {
                            switchIf(argInstance,f,null,map);
                        }
                        coll.add(argInstance);
                    }
                    field.set(o,coll);
                }
            }
        }else{
            // 基本數組類型
            Class<?> componentType = ((Class<?>) genericType).getComponentType();
            if (componentType != null){
                // 數組類型,例如:int[] string[] entity[]
                copyMappingArray(o,field,componentType,extractValue);
            }else {
                // 基本類型,不包含泛型的類型 ;基本類型或單類型
                boolean b = copyMappingField(o, field, (Class<?>) genericType, extractValue);
                if(!b){
                    log.error("不是基本類型,準備再次進行解刨 {}",genericType);
                    // 不是基本類型,則進行再次解拋
                    Class<?> genericCls = ((Class<?>) genericType);
                    Object instance = genericCls.newInstance();
                    Field[] allFields = MyReflectUtils.getAllFields(genericCls);
                    for (Field f : allFields) {
                        switchIf(instance,f,null,valueMap);
                    }
                }
            }
        }
    }


    /**
     * 處理基本數組形式的copy映射,例如:int[],String[],Long[],實體類[],POJO[];
     *  粗略的說明就是:將 expectValue經過解析,返回數組形式的值對象,設置到o對象裏的field字段中
     * @param o 需要設置對象的實例
     * @param field 當前處理的字段
     * @param typeCls 字段的類型
     * @param expectValue 期望的值
     * @throws IllegalAccessException
     * @throws SQLException
     * @throws InstantiationException
     */
    @SuppressWarnings("DuplicatedCode")
    private void copyMappingArray(Object o, Field field, Class<?> typeCls, Object expectValue) throws IllegalAccessException, SQLException, InstantiationException {
        Object[] oS = (Object[]) Array.newInstance(typeCls,0);
        if(BASE_TYPE_SET.contains(typeCls)){
            List<Object> objects = parsedValueIsArray(field.getName(), expectValue);
            field.set(o,objects.toArray(oS));
        }else{
            List<Map<String, Object>> maps = parsedValueIsArrayMap(field.getName(), expectValue);
            ArrayList<Object> objects = new ArrayList<>();
            Object instance;
            for (Map<String, Object> map : maps) {
                instance = typeCls.newInstance();
                Field[] allFields = MyReflectUtils.getAllFields(typeCls);
                for (Field f : allFields) {
                    switchIf(instance,f,null,map);
                }
                objects.add(instance);
            }
            field.set(o,objects.toArray(oS));
        }
    }

    /**
     * 賦值映射普通字段
     */
    private boolean copyMappingField(Object o,Field field,Class<?> fieldTypeCls,Object value) throws IllegalAccessException {
        if(BASE_TYPE_SET.contains(fieldTypeCls)){
            if(value != null){
                value = ConvertUtils.convert(value, fieldTypeCls);
            }
            field.set(o,value);
            return true;
        }
        return false;
    }

    /**
     * 提取ResultSet 或 valueMap中的值(二選一),根據字段名稱進行獲取
     * @param rs 結果集 (二選一)
     * @param valueMap 值Map,保存字段的名稱和值 (二選一)
     * @param fieldName 字段名稱
     * @return
     * @throws SQLException
     */
    @SuppressWarnings({"DuplicatedCode"})
    private Object extractValue(ResultSet rs,Map<String,Object> valueMap,String fieldName) throws SQLException {
        String underscoreName = fieldName;
        Matcher matcher = COMPILE.matcher(underscoreName);
        while (matcher.find()){
            underscoreName = underscoreName.replace(matcher.group(1),"_" + matcher.group(1).toLowerCase());
        }
        String[] keys = new String[]{fieldName,underscoreName};
        for (String key : keys) {
            try{
                return rs != null ? rs.getObject(key) : valueMap !=null && valueMap.containsKey(key) ? valueMap.get(key) : null;
            }catch (SQLSyntaxErrorException e) {
                // 什麼也不做,繼續遍歷key獲取字典
                if (e.getErrorCode() != 1054){
                    throw e;
                }
            }
        }
        log.debug("字段 {}從ResultSet中無法獲取到值,請注意是否是設計如此", fieldName);
        return null;
    }

    public void test(){
        System.out.println(this.getClass());
    }

    @SuppressWarnings("unchecked")
    @Override
    public T mapRow(ResultSet rs, int rowNum) throws SQLException {
        try {
            Type genericSuperclass = this.getClass().getGenericSuperclass();
            if(genericSuperclass instanceof ParameterizedType){
                ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
                return autoMapping(rs, (Class<T>) parameterizedType.getActualTypeArguments()[0]);
            }else {
                log.warn("泛型未聲明,使用 this.getClass() 獲取注入的Class對象");
                return autoMapping(rs, (Class<T>) this.getClass());
            }
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) throws IllegalAccessException, SQLException, InstantiationException {
//        Matcher virtualUserName = compile.matcher("virtualUserName");
//        System.out.println(virtualUserName.find());
//        System.out.println(virtualUserName.find());
//        System.out.println(virtualUserName.find());
//        System.out.println(virtualUserName.find());
//
//        String name = "virtualUserName";
//        String underscoreName = name;
//        Matcher matcher = compile.matcher(name);
//        while (matcher.find()){
//            underscoreName = underscoreName.replace(matcher.group(1),"_" + matcher.group(1).toLowerCase());
//        }
//        System.out.println("underscoreName:" + underscoreName);

//        TestResultSet testResultSet = new TestResultSet();
//        TestEntity testEntity = new TestEntity();
//        TestEntity testEntity1 = testEntity.autoMapping(testResultSet, TestEntity.class);
//        System.out.println("testEntity1: " + testEntity1);

        System.err.println(boolean.class);
        System.err.println(Boolean.class);
        System.err.println(Boolean.TYPE);
        System.err.println(Integer.TYPE);
        System.err.println(Integer.class);
        System.err.println(Integer[].class);
        System.err.println(int[].class);
        System.err.println(float[].class);
        System.err.println(Float[].class);

        ArticleInfoRepoModel articleInfoRepoModel = new ArticleInfoRepoModel();
        articleInfoRepoModel.mapRow(null,1);


    }




    @Getter
    @Setter
    @ToString
    static class TestEntity extends RowMapperAutoMapping<TestEntity> {

        private String id;

        private String name;

        private Integer age;

        private Long createTime;

        private String[] authors;

        private TestCategoryEntity[] testCategoryEntities;

        private List<TestCategoryEntity> testCategoryEntityList;

        @Override
        protected List<Object> parsedValueIsArray(String fieldName, Object value) {
            ArrayList<Object> objects = new ArrayList<>();
            if("authors".equals(fieldName)){
                String[] split = value.toString().split(",");
                for (String s : split) {
                    objects.add(s);
                }
            }
            return objects;
        }

        @Override
        protected List<Map<String, Object>> parsedValueIsArrayMap(String fieldName, Object value) {
            ArrayList<Map<String, Object>> maps = new ArrayList<>();
            HashMap<String, Object> hashMap = new HashMap<>();
            hashMap.put("id","小飛機_id");
            hashMap.put("name","小飛機撞大象");
            maps.add(hashMap);

            hashMap = new HashMap<>();
            hashMap.put("id","小火車_id");
            hashMap.put("name","小火車開飛機");
            maps.add(hashMap);
            return maps;
        }
    }

    @Data
    static class TestCategoryEntity{

        private String id;

        private String name;

    }



    static class TestResultSet extends JdbcRowSetImpl {

        private Map<String,Object> map = new HashMap<>();

        public TestResultSet() {
            map.put("id","001_id");
            map.put("name","我是iemo");
            map.put("age",25);
            map.put("createTime",System.currentTimeMillis());
            map.put("authors","小王作者,小惡魔作者");
            map.put("testCategoryEntities","001:喜哦,002:小王");
        }


        @Override
        public Object getObject(String columnLabel) throws SQLException {
            return map.get(columnLabel);
        }
    }
}
View Code

 

 

 

這是反射工具類,可以獲取範圍內的方法的字段列表,沒有加鎖,當併發高的情況下,可能會有問題吧,後期優化即可,每個類的反射出的字段都會進行緩存下來,方便下次直接返回

package cn.dmahz.utils;

import cn.dmahz.anno.IgnoreField;
import cn.dmahz.anno.ManyChild;
import cn.dmahz.anno.OneParent;
import cn.dmahz.anno.PrimaryId;
import cn.dmahz.excep.*;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.annotation.*;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
public class MyReflectUtils {

    private static final Field[] ARRAY_FIELDS = new Field[0];

    private static final Map<String, Map<String, Field>> CACHE_FIELD = new ConcurrentHashMap<>();
    private static final Map<String, Map<String, Field>> CACHE_AUDIT_FIELD = new ConcurrentHashMap<>();

    /*鎖對象實例*/
    private static final Object CACHE_LOCK = new Object();
    private static final Object CACHE_AUDIT_LOCK = new Object();


    private static final Map<String, Map<String, Method>> CACHE_METHOD = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        Method createBy = getMethodInMapper("cn.dmahz.dao.mapper.RelatedBookCategoryMapper.test_save");
        System.out.println(createBy);

        System.out.println(Boolean.class.getSimpleName());

    }

    /**
     * 獲取映射器的中的方法,僅適用於Mapper接口中的方法
     * @param mapperId 例如:cn.dmahz.dao.mapper.RelatedBookCategoryMapper.test_save
     * @return
     */
    public static Method getMethodInMapper(String mapperId){
        int lastIndexOf = mapperId.lastIndexOf(".");
        // 包名.類名
        String classFullName = mapperId.substring(0,lastIndexOf);
        // 方法名
        String methodName = mapperId.substring(lastIndexOf + 1);
        return getMethodInInterface(classFullName,methodName);
    }

    /**
     * 獲取指定接口的方法對象
     * @param className 類全路徑字符串;例如:class.getName()
     * @param methodName 方法名稱
     * @return
     */
    @SneakyThrows
    public static Method getMethodInInterface(String className, String methodName){
        Class<?> cls = Class.forName(className);

        String clsName = cls.getName();
        if(!CACHE_METHOD.containsKey(clsName)){
            CACHE_METHOD.put(clsName,new ConcurrentHashMap<>());
        }
        Map<String, Method> methodMap = CACHE_METHOD.get(clsName);
        if(!methodMap.isEmpty()){
            return methodMap.get(methodName);
        }else {
            Class<?> targetCls = cls;
            Method targetMethod = null;
            do {
                Method[] declaredMethods = targetCls.getDeclaredMethods();
                for (Method method:declaredMethods){
                    String name = method.getName();
                    if(!methodMap.containsKey(name)){
                        if(name.equals(methodName))
                            targetMethod = method;
                        methodMap.put(name,method);
                    }
                }
            } while (targetCls.getInterfaces().length> 0 && (targetCls = targetCls.getInterfaces()[0]) != null);
            return targetMethod;
        }
    }




    /**
     * 獲取cls中的全部的審計字段
     *
     * @param cls
     * @return
     */
    public static Field[] getAllAuditFields(Class<?> cls){
        String auditClsKey = cls.getName();
        if(!CACHE_AUDIT_FIELD.containsKey(auditClsKey)) {
            synchronized (CACHE_AUDIT_LOCK) {
                if(CACHE_AUDIT_FIELD.containsKey(auditClsKey)){
                    return CACHE_AUDIT_FIELD.get(auditClsKey).values().toArray(ARRAY_FIELDS);
                } else {
                    ConcurrentHashMap<String, Field> fieldMap = new ConcurrentHashMap<>(7);
                    Field[] allFields = getAllFields(cls);
                    for (Field field : allFields) {
                        if (field.isAnnotationPresent(CreatedBy.class) || field.isAnnotationPresent(CreatedDate.class)
                                || field.isAnnotationPresent(LastModifiedBy.class) || field.isAnnotationPresent(LastModifiedDate.class)
                                || field.isAnnotationPresent(Id.class)) {
                            fieldMap.put(field.getName(), field);
                        }
                    }
                    CACHE_AUDIT_FIELD.put(auditClsKey, fieldMap);
                }
            }
        }
        return CACHE_AUDIT_FIELD.get(auditClsKey).values().toArray(ARRAY_FIELDS);
    }

    /**
     * 獲取除Object以外的所有字段數組
     * @param cls
     * @return
     */
    public static Field[] getAllFields(Class<?> cls){
        return getAllFields(cls, null);
    }

    /**
     * 獲取截至endClass的所有字段列表,忽略Object的所有字段
     * @param cls 實體類對象
     * @param endClass 停止類,包含當前類所屬字段
     * @return
     */
    public static Field[] getAllFields(Class<?> cls,Class<?> endClass){
        Class<?> targetClass = cls;
        Map<String, Field> cacheField = getCacheField(targetClass.getName());
        if(cacheField.isEmpty()){
            do {
                Field[] declaredFields = targetClass.getDeclaredFields();
                for(Field field:declaredFields){
                    field.setAccessible(true);
                    cacheField.put(field.getName(),field);
                }
            }while ((targetClass = targetClass.getSuperclass()) != null &&
                    (!(endClass != null && targetClass.isAssignableFrom(endClass)) && !targetClass.isAssignableFrom(Object.class))
            );
        }
        return cacheField.values().toArray(ARRAY_FIELDS);
    }

    /**
     * 獲取當前類中指定字段名稱的對象
     * @param cls
     * @param fieldName
     * @return
     */
    public static Field getDeclaredFields(Class<?> cls,String fieldName){
        Class<?> targetClass = cls;
        Map<String, Field> cacheField = getCacheField(targetClass.getName());
        if(cacheField.containsKey(fieldName)){
            return cacheField.get(fieldName);
        }
        do {
            Field declaredField = null;
            try {
                declaredField = targetClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                // 這裏不處理異常
            }
            if(declaredField != null){
                ReflectionUtils.makeAccessible(declaredField);
                putCacheField(cls.getName(),fieldName,declaredField);
                return declaredField;
            }
        }while ((targetClass = targetClass.getSuperclass()) != null && !targetClass.isAssignableFrom(Object.class));
        return null;
    }

    private static void putCache(String clsName,Map<String,Field> fieldMap){
        Map<String, Field> cacheField = getCacheField(clsName);
        cacheField.putAll(fieldMap);
    }

    private static void putCacheField(String clsName,String fieldName,Field field){
        Map<String, Field> cacheField = getCacheField(clsName);
        cacheField.put(fieldName,field);
    }

    private static Map<String,Field> getCacheField(String clsName){
        Map<String, Field> stringFieldMap = CACHE_FIELD.get(clsName);
        if(ObjectUtils.isEmpty(stringFieldMap)){
            stringFieldMap = new ConcurrentHashMap<>(7);
            CACHE_FIELD.put(clsName,stringFieldMap);
        }
        return stringFieldMap;
    }


    public static String convertGetMethodName(Field field){
        char[] chars = field.getName().toCharArray();
        chars[0] = String.valueOf(chars[0]).toUpperCase().toCharArray()[0];
        if(boolean.class.isAssignableFrom(field.getType())){
            return "is".concat(String.valueOf(chars));
        }
        return "get".concat(String.valueOf(chars));
    }

    public static String convertSetMethodName(Field field){
        char[] chars = field.getName().toCharArray();
        chars[0] = String.valueOf(chars[0]).toUpperCase().toCharArray()[0];
        return "set".concat(String.valueOf(chars));
    }

    public static <T,VO> VO convertToVO(T source,Class<VO> target){
        if(source == null){
            return null;
        }
        return doConvertToVO(source,target,null);
    }

    /**
     * 轉換爲VO對象
     * @param sourceList
     * @param target
     * @return
     */
    public static <T,VO> List<VO> convertToVO(List<T> sourceList,Class<VO> target){
        final Field[] allFields = getAllFields(target);
        List<VO> voList = sourceList.parallelStream().map(source -> doConvertToVO(source,target,allFields)).filter(Objects::nonNull).sequential().collect(Collectors.toList());
        return voList;
    }

    /**
     * 將源源對象轉換成VO實體對象,相同字段則進行賦值
     * @param source
     * @param target
     * @param targetFields
     * @param <T>
     * @param <VO>
     * @return
     */
    private static <T,VO> VO doConvertToVO(T source,Class<VO> target,Field[] targetFields){
        Field[] allFields = targetFields;
        if(allFields == null){
            allFields = getAllFields(target);
        }
        VO vo = null;
        try {
            vo = target.newInstance();
            for (Field field : allFields) {
                ReflectionUtils.makeAccessible(field);
                if (!writePrimaryIdField(source, vo, field) && !writeOneParentField(source, vo, field) && !writeManyChildField(source, vo, field)) {
                    writeOrdinaryField(source,source.getClass(), vo, field);
                }
            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return vo;
    }


    /**
     * 將目標實體以父子關係鏈接起來
     * @param <T>
     * @param sourceList 源對象列表
     * @param targetCls 目標Class
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @return
     */
    public static <T> List<T> linkRelationshipTree(List<?> sourceList, Class<T> targetCls) {

        List<CombinationData> combinationDataList = new ArrayList<>();

        HashMap<String, T> resultMap = new HashMap<>(sourceList.size());

        HashMap<String, CombinationData.Child> childHashMap = new HashMap<>(sourceList.size());

        boolean parentMode = false;
        boolean childMode = false;

        Set<String> cacheFieldSignature = new HashSet<>();

        for (Object source : sourceList) {
            cacheFieldSignature.clear();
            Class<?> sourceClass = source.getClass();
            T newInstance;
            try {
                newInstance = targetCls.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                CannotInstantiationException cannotInstantiationException = new CannotInstantiationException("不能實例化此對象:" + targetCls.getName());
                cannotInstantiationException.addSuppressed(e);
                throw cannotInstantiationException;
            }

            CombinationData combinationData = new CombinationData();
            combinationData.setSource(source);
            combinationData.setTarget(newInstance);

            ReflectionUtils.doWithFields(targetCls,field -> {
                ReflectionUtils.makeAccessible(field);

                String fieldSignature = field.getType().getTypeName().concat("->").concat(field.getName());
                if(cacheFieldSignature.contains(fieldSignature)){
                    return;
                }

                /**
                 * 當字段不是被 @PrimaryId、@OneParent、@MaryChild 標註的話,會拋出異常
                 */
                if (!writePrimaryIdField(sourceClass, combinationData, field) && !writeOneParentField(source, sourceClass, combinationData, field) && !writeManyChildField(combinationData, source, sourceClass, field)) {
                    writeOrdinaryField(source, sourceClass, newInstance, field);
                }

                cacheFieldSignature.add(fieldSignature);

            }, ReflectionUtils.COPYABLE_FIELDS);
            String primaryIdValue = combinationData.getIdValue();

            resultMap.put(primaryIdValue,newInstance);

            CombinationData.Child child = combinationData.getChild();
            if(ObjectUtils.isNotEmpty(child)){
                childHashMap.put(primaryIdValue,child);
            }

            //判斷模式
            if(ObjectUtils.isNotEmpty(combinationData.getParent())){
                parentMode = true;
            }
            if(ObjectUtils.isNotEmpty(combinationData.getChild())){
                childMode = true;
            }

            combinationDataList.add(combinationData);
        }

        if(parentMode && childMode){
            throw new CircularReferenceException(targetCls.getSimpleName().concat("循環引用異常,請使用 {@OneParent|@ManyChild} 其中一個註解進行標識"));
        }

        Collection<T> collection = processParseConstituteTree(parentMode, childMode, combinationDataList, resultMap, childHashMap);

        return Collections.unmodifiableList(new ArrayList<>(collection));
    }

    @ToString
    private static class CombinationData {
        /**
         * 提取數據的源對象
         */
        @Getter
        @Setter
        private Object source;

        /**
         * 設置數據的目標對象
         */
        @Getter
        @Setter
        private Object target;

        /**
         * 源對象的主鍵ID
         */
        @Getter
        private Field sourcePrimaryIdField;

        @Getter
        private Field targetPrimaryIdField;

        /**
         * 當前target和source對應的主鍵id值
         */
        private Object idValue;

        public <T> T getIdValue(){
            return (T) idValue;
        }


        /**
         * 設置主鍵ID字段
         * @param sourceField 源主鍵ID字段
         * @param targetField 目標主鍵ID字段
         */
        public void setPrimaryIdField(Field sourceField,Field targetField){
            this.sourcePrimaryIdField = sourceField;
            this.targetPrimaryIdField = targetField;
            try {
                Object o = CombinationData.this.sourcePrimaryIdField.get(source);
                CombinationData.this.targetPrimaryIdField.set(target,o);
                this.idValue = o;
            } catch (IllegalAccessException e) {
                throw new IllegalAccessObjectException("非法訪問對象字段");
            }catch (NullPointerException e){
                throw new NullPointerException("未提供 @PrimaryId 標註主鍵ID字段");
            }
        }


        @Getter
        private Parent parent;

        public Parent newInstanceParent(){
            if(ObjectUtils.isEmpty(this.parent)){
                this.parent = new Parent();
            }
            return parent;
        }

        private class Parent {

            /**
             * 目標對象的父字段對象
             */
            @Getter
            @Setter
            private Field targetParentField;

            @Getter
            private Type targetTypes;

            /**
             * 源對象的父ID值
             */
            @Getter
            @Setter
            private Object parentId;

            public void setTargetParentField(Field targetParentField) {
                this.targetParentField = targetParentField;
                this.targetTypes = targetParentField.getGenericType();
            }

            public void setTargetParentField(Object value) throws IllegalAccessException {
                targetParentField.set(CombinationData.this.target,value);
            }

        }

        @Getter
        private Child child;
        public Child newInstanceChild(){
            if(ObjectUtils.isEmpty(this.child)){
                this.child = new Child();
            }
            return child;
        }

        private class Child {

            /**
             * 當前子屬性集合對象的字段,最終要往這裏添加值
             */
            @Getter
            private Field targetField;

            /**
             * 父ID值,後期進行計算
             */
            @Getter
            @Setter
            private Object parentId;

            /**
             * 當前對象對應的子ID集合,
             * 需要反向思考父子關係
             */
            @Getter
            private List<String> childIdList = new ArrayList<>();

            public void addChildId(String childId){
                childIdList.add(childId);
            }

            public void setTargetField(Field targetField) throws IllegalAccessException {
                this.targetField = targetField;
                //設置泛型的參數化類型
                Type genericType = targetField.getGenericType();
                ParameterizedType parameterizedType = (ParameterizedType) genericType;
                Type typeArgument = parameterizedType.getActualTypeArguments()[0];
                this.genericType = typeArgument;
                //獲取目標對象的子集合對象
                Object o = null;
                try {
                    o = targetField.get(CombinationData.this.target);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

                if(ObjectUtils.isEmpty(o)){
                    o = new ArrayList<>();
                    this.targetField.set(CombinationData.this.target,o);
                }

                this.list = (List<Object>) o;
            }


            /**
             * 目標對象的子集合屬性引用
             */
            private List<Object> list;

            /**
             * 泛型的類型
             */
            private Type genericType;

            public void addChild(Object value){
                Class<?> genericType = (Class<?>) this.genericType;
                if(genericType.isAssignableFrom(value.getClass())){
                    list.add(value);
                } else {
                    throw new TypeNotMatchException(String.format("提供的數據類型爲:%s;需要的類型:%s",value.getClass(),this.genericType.getClass()));
                }
            }
        }

    }


    /**
     * 寫入普通字段
     */
    private static boolean writeOrdinaryField(Object source,Class<?> sourceClass,Object target ,Field field) throws IllegalAccessException {
        //是否需要忽略當前字段,只有目標字段作爲普通字段時才具備此效果
        if(field.isAnnotationPresent(IgnoreField.class)){
            return true;
        }
        String fieldName = field.getName();
        Field f = getDeclaredFields(sourceClass,fieldName);
        if(ObjectUtils.isEmpty(f)){
            log.debug("源對象[{}] -> 字段[{}] 不存在",sourceClass.getName(),fieldName);
//            throw new UnableProcessThis("無法處理當前字段(如果需要忽略,請使用@IgnoreField註解在 ["+target.getClass().getName()+"] 中進行標註):"+field.toGenericString());
            return false;
        }else {
            Object srcValue = f.get(source);
            field.set(target, srcValue);
            return true;
        }
    }

    private static boolean writePrimaryIdField(Class<?> sourceClass,CombinationData combinationData,Field field){
        if (field.isAnnotationPresent(PrimaryId.class)){
            Field f = getDeclaredFields(sourceClass, field.getName());
            combinationData.setPrimaryIdField(f,field);
            return true;
        }
        return false;
    }

    private static boolean writePrimaryIdField(Object source,Object target ,Field targetField) throws IllegalAccessException {
        if (targetField.isAnnotationPresent(PrimaryId.class)){
            Field f = getDeclaredFields(source.getClass(), targetField.getName());
            targetField.set(target,f.get(source));
            return true;
        }
        return false;
    }

    private static boolean writeOneParentField(Object source,Class<?> sourceClass,CombinationData combinationData,Field field){
        OneParent oneParent = AnnotationUtils.getAnnotation(field, OneParent.class);
        if(ObjectUtils.isNotEmpty(oneParent)){
            Field f = MyReflectUtils.getDeclaredFields(sourceClass,oneParent.parentIdValueFieldName());
            Object value = ReflectionUtils.getField(f, source);
            CombinationData.Parent parent = combinationData.newInstanceParent();
            parent.setParentId(value);
            parent.setTargetParentField(field);
            return true;
        }
        return false;
    }

    private static boolean writeOneParentField(Object source,Object target ,Field targetField) throws IllegalAccessException {
        OneParent oneParent = AnnotationUtils.getAnnotation(targetField, OneParent.class);
        if(ObjectUtils.isNotEmpty(oneParent)){
            Field f = MyReflectUtils.getDeclaredFields(source.getClass(),oneParent.parentIdValueFieldName());
            Object value = ReflectionUtils.getField(f, source);
            targetField.set(target,value);
            return true;
        }
        return false;
    }

    private static boolean writeManyChildField(CombinationData combinationData,Object source,Class<?> sourceClass,Field field) throws IllegalAccessException {
        ManyChild manyChild = AnnotationUtils.getAnnotation(field, ManyChild.class);
        if(ObjectUtils.isNotEmpty(manyChild)){
            //從源對象獲取父ID
            Field f = MyReflectUtils.getDeclaredFields(sourceClass,manyChild.parentIdValueFieldName());
            Object value = ReflectionUtils.getField(f, source);
            CombinationData.Child child = combinationData.newInstanceChild();
            child.setTargetField(field);
            child.setParentId(value);
            return true;
        }
        return false;
    }

    private static boolean writeManyChildField(Object source,Object target ,Field targetField) throws IllegalAccessException {
        ManyChild manyChild = AnnotationUtils.getAnnotation(targetField, ManyChild.class);
        if(ObjectUtils.isNotEmpty(manyChild)){
            //從源對象獲取父ID
            Field f = MyReflectUtils.getDeclaredFields(source.getClass(),manyChild.parentIdValueFieldName());
            Object value = ReflectionUtils.getField(f, source);
            targetField.set(target,value);
            return true;
        }
        return false;
    }

    /**
     * 將列表數據構成樹
     * @param finalParentMode
     * @param finalChildMode
     * @param combinationDataList
     * @param rootMap
     * @param childHashMap
     * @param <T>
     * @return
     */
    private static <T> Collection<T> processParseConstituteTree(final boolean finalParentMode,final boolean finalChildMode,
                                                   List<CombinationData> combinationDataList,
                                                   HashMap<String, T> rootMap,
                                                       HashMap<String, CombinationData.Child> childHashMap
                                                   ){
        //排除的ID集合,從resultMap中根據key進行刪除
        final List<Object> excludeIdList = new ArrayList<>();

        combinationDataList.forEach(combinationData -> {
            //處理父級關係
            CombinationData.Parent parent = combinationData.getParent();
            if(ObjectUtils.isNotEmpty(parent)){
                Object parentId = parent.getParentId();
                T t = rootMap.get(parentId);
                if(ObjectUtils.isNotEmpty(t)){
                    try {
                        combinationData.getParent().setTargetParentField(t);
                        if(finalParentMode && !finalChildMode){
                            excludeIdList.add(parentId);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        if(finalChildMode){
            excludeIdList.clear();
        }

        //解析子級列表關係
        childHashMap.forEach((id, child) -> {
            Object parentId = child.getParentId();
            if(ObjectUtils.isNotEmpty(parentId)){
                CombinationData.Child c = childHashMap.get(parentId);
                if(ObjectUtils.isNotEmpty(c)){
                    T currentT = rootMap.get(id);
                    if(ObjectUtils.isEmpty(currentT)){
                        throw new InvalidPrimaryIdValueException("無效主鍵ID值異常;不能找到指定主鍵值對應的實體;主鍵ID值爲:".concat(id));
                    }
                    c.addChild(currentT);
                    //這裏需要記錄排除的ID列表
                    if(finalChildMode){
                        excludeIdList.add(id);
                    }
                }
            }
        });

        //移除 resultMap 裏的指定ID鍵對象
        // 如果有子級列表關係存在,則只返回首層,否則,返回最後一個子集
        excludeIdList.forEach(rootMap::remove);

        return rootMap.values();
    }


}
View Code

 

 

特殊的就是 

org.apache.commons.beanutils.ConvertUtils;  這個是commons包中的,來源去maven repo查詢即可


如果有問題留言討論


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章