• 使用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查询即可


    如果有问题留言讨论


    复制请注明出处,在世界中挣扎的灰太狼
  • 相关阅读:
    osc搜索引擎框架search-framework,TngouDB,gso,
    当Scheduler拿不到url的 时候,不能立即退出
    摘要算法CRC8、CRC16、CRC32,MD2 、MD4、MD5,SHA1、SHA256、SHA384、SHA512,RIPEMD、PANAMA、TIGER、ADLER32
    sha256
    非对称加密RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等。使用最广泛的是RSA算法
    3个著名加密算法(MD5、RSA、DES)的解析
    DOM
    面向对象
    抽象工厂在ADO.Net中的应用
    Provider Pattern提供者模式和策略模式
  • 原文地址:https://www.cnblogs.com/XingXiaoMeng/p/14399436.html
Copyright © 2020-2023  润新知