使用@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); } } }
這是反射工具類,可以獲取範圍內的方法的字段列表,沒有加鎖,當併發高的情況下,可能會有問題吧,後期優化即可,每個類的反射出的字段都會進行緩存下來,方便下次直接返回
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(); } }
特殊的就是
org.apache.commons.beanutils.ConvertUtils; 這個是commons包中的,來源去maven repo查詢即可
如果有問題留言討論