在后台管理系统中经常会有需要记录用户操作日志的需求,其中需要最好能记录用户修改了哪些字段的数据(变更前,变更后),这个时候就需要抽取一个工具的类来处理这件事。
20180105进行优化升级:1、增加对基本类型的集合的对比。2、优化反射的缓存。3、增加dto+集合的混合对比方法。
结果样例:
[{ "column": "name", "difference": { "newValue": "李四", "oldValue": "张三" } }, { "column": "arr", "difference": { "newValue": [{ "column": "delete", "difference": { "newValue": null, "oldValue": [1] } }, { "column": "add", "difference": { "newValue": [4], "oldValue": null } } ], "oldValue": null } }]
工具类:
1 import java.io.Serializable; 2 import java.lang.reflect.Field; 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 import java.math.BigDecimal; 6 import java.util.ArrayList; 7 import java.util.Collection; 8 import java.util.HashMap; 9 import java.util.HashSet; 10 import java.util.List; 11 import java.util.Map; 12 import java.util.Set; 13 import java.util.regex.Matcher; 14 import java.util.regex.Pattern; 15 16 import org.reflections.ReflectionUtils; 17 18 import com.alibaba.fastjson.JSON; 19 import com.alibaba.fastjson.serializer.SerializerFeature; 20 21 import lombok.AllArgsConstructor; 22 import lombok.Builder; 23 import lombok.Getter; 24 import lombok.NoArgsConstructor; 25 import lombok.Setter; 26 import lombok.extern.slf4j.Slf4j; 27 28 /** 29 * 比较2个对象的值的不同的情况 30 * 31 * 注意:对声明值比较类为基本类型的但传入非基本类型的话不保证程序和数据的正确性。 32 * 33 * 目前支持的比较类型有: 34 * 1、普通dto(不包含复杂对象)的比较 35 * 可用的方法有: 36 * { @link CompareDifferenceUtil#isDifference(Object, Object)} 37 * { @link CompareDifferenceUtil#isDifferenceSelective(Object, Object)} 38 * { @link CompareDifferenceUtil#compare(Object, Object)} 39 * { @link CompareDifferenceUtil#compareSelective(Object, Object)} 40 * { @link CompareDifferenceUtil#getDiffernceList(Object, Object)} 41 * { @link CompareDifferenceUtil#getDiffernceListSelective(Object, Object)} 42 * 2、普通基本类型的集合的比较 43 * 可用的方法有: 44 * { @link CompareDifferenceUtil#isDifference(Collection, Collection)} 45 * { @link CompareDifferenceUtil#compareCollection(Collection, Collection)} 46 * { @link CompareDifferenceUtil#compareCollectionSelective(Collection, Collection)} 47 * 3、普通dto(不包含复杂对象) + 普通基本类型的集合的比较 48 * 可用的方法有: 49 * { @link CompareDifferenceUtil#compare(Object, Object, CompareCollectionDto...)} 50 * { @link CompareDifferenceUtil#compareSelective(Object, Object, CompareCollectionDto...)} 51 * 4、主从一对一的比较 52 * 可用的方法有: 53 * { @link CompareDifferenceUtil#compareMainAndDetail(Object, Object, String, Object, Object)} 54 * { @link CompareDifferenceUtil#compareMainAndDetailSelective(Object, Object, String, Object, Object)} 55 * 56 * 57 * @author yangzhilong 58 */ 59 @Slf4j 60 public class CompareDifferenceUtil { 61 private static final String ADD = "add"; 62 private static final String UPDATE = "update"; 63 private static final String DELETE = "delete"; 64 private static final SetignoreFileds = new HashSet<>(5); 65 66 private static final Map > fieldCache = new HashMap<>(); 67 private static final Map > methodCache = new HashMap<>(); 68 69 static { 70 ignoreFileds.add("serialVersionUID"); 71 ignoreFileds.add("createBy"); 72 ignoreFileds.add("createTime"); 73 ignoreFileds.add("updateBy"); 74 ignoreFileds.add("updateTime"); 75 } 76 77 /** 78 * 比较2个对象是否有差异 79 * @param oldObj 80 * @param newObj 81 * @return 82 */ 83 public static boolean isDifference(Object oldObj, Object newObj) { 84 return !getDiffernceList(oldObj, newObj, true, false).isEmpty(); 85 } 86 87 /** 88 * 比较2个对象是否有差异(新增中的属性为null的话跳过比较该属性) 89 * @param oldObj 90 * @param newObj 91 * @return 92 */ 93 public static boolean isDifferenceSelective(Object oldObj, Object newObj) { 94 return !getDiffernceList(oldObj, newObj, true, true).isEmpty(); 95 } 96 97 /** 98 * 比较2个集合是否有差异(基本类型) 99 * @param oldCollection100 * @param newCollection101 * @return102 */103 public static boolean isDifference(Collection oldCollection, Collection newCollection) {104 return !compareCollectionList(oldCollection, newCollection).isEmpty();105 }106 107 /**108 * 得到变化的列的数据109 * @param oldObj110 * @param newObj111 * @return112 */113 public static List getDiffernceList(Object oldObj, Object newObj) {114 return getDiffernceList(oldObj, newObj, false, false);115 }116 117 /**118 * 得到变化的列的数据(新增中的属性为null的话跳过比较该属性)119 * @param oldObj120 * @param newObj121 * @return122 */123 public static List getDiffernceListSelective(Object oldObj, Object newObj) {124 return getDiffernceList(oldObj, newObj, false, true);125 }126 127 /**128 * 比较主从对象的复杂类型的数据(主从一对一关系)129 * @param oldMainObj 主对象的老对象130 * @param newMainObj 助对象的新对象131 * @param detailAttrName 主对象中detail的属性名称132 * @param oldDetailObj 明细对象的老对象(复杂类型的对象,但非集合)133 * @param newDetailObj 明细对象的新对象(复杂类型的对象,但非集合)134 * @return135 */136 public static String compareMainAndDetail(Object oldMainObj, Object newMainObj, String detailAttrName, Object oldDetailObj, Object newDetailObj) {137 return aggregateDataAndConvertToJson(getDiffernceList(oldMainObj, newMainObj), detailAttrName , getDiffernceList(oldDetailObj, newDetailObj));138 }139 140 /**141 * 比较主从对象的复杂类型的数据(主从一对一关系)142 * @param oldMainObj 主对象的老对象143 * @param newMainObj 助对象的新对象144 * @param detailAttrName 主对象中detail的属性名称145 * @param oldDetailObj 明细对象的老对象(复杂类型的对象,但非集合)146 * @param newDetailObj 明细对象的新对象(复杂类型的对象,但非集合)147 * @return148 */149 public static String compareMainAndDetailSelective(Object oldMainObj, Object newMainObj, String detailAttrName, Object oldDetailObj, Object newDetailObj) {150 return aggregateDataAndConvertToJson(getDiffernceListSelective(oldMainObj, newMainObj), detailAttrName , getDiffernceListSelective(oldDetailObj, newDetailObj));151 }152 153 /**154 * 得到2个相同对象的相同字段的不同的值155 * 156 * @param oldObj 变更前对象,不能为null157 * @param newObj 变更后对象,不能为null158 * @return159 */160 public static String compare(Object oldObj, Object newObj) {161 List result = getDiffernceList(oldObj, newObj);162 if(!result.isEmpty()) {163 return JSON.toJSONString(result, SerializerFeature.WriteMapNullValue);164 }165 return null;166 }167 168 /**169 * 得到2个相同对象的相同字段的不同的值,newObj中有属性为null,就跳过这个属性170 *171 * @param oldObj 变更前对象,不能为null172 * @param newObj 变更后对象,不能为null173 * @return174 */175 public static String compareSelective(Object oldObj, Object newObj) {176 List result = getDiffernceListSelective(oldObj, newObj);177 if(!result.isEmpty()) {178 return JSON.toJSONString(result, SerializerFeature.WriteMapNullValue);179 }180 return null;181 }182 183 /**184 * 得到2个对象以及相关list的差异185 * @param 186 * @param oldObj187 * @param newObj188 * @param array 多个基本类型的集合的包装类189 * @return 190 */191 @SuppressWarnings("unchecked")192 public static String compare(Object oldObj, Object newObj, CompareCollectionDto ... array) {193 return baseCompare(oldObj, newObj, false, array);194 }195 196 /**197 * 得到2个对象以及相关list的差异,如果有新list集合为空,则忽略此属性198 * @param 199 * @param oldObj200 * @param newObj201 * @param array 多个基本类型的集合的包装类202 * @return203 */204 @SuppressWarnings("unchecked")205 public static String compareSelective(Object oldObj, Object newObj, CompareCollectionDto ... array) {206 return baseCompare(oldObj, newObj, true, array);207 }208 209 /**210 * 比较2个字符串集合的差异(只支持基本类型)211 * @param 212 * @param oldCollection213 * @param newCollection214 * @return215 */216 public static String compareCollection(Collection oldCollection, Collection newCollection) {217 List result = compareCollectionList(oldCollection, newCollection);218 if(result.isEmpty()) {219 return null;220 }221 return JSON.toJSONString(result, SerializerFeature.WriteMapNullValue);222 }223 224 /**225 * 比较2个字符串集合的差异(只支持基本类型),如果newCollection为null,则忽略比较226 * @param 227 * @param oldCollection228 * @param newCollection229 * @return230 */231 public static String compareCollectionSelective(Collection oldCollection, Collection newCollection) {232 List result = compareCollectionListSelective(oldCollection, newCollection);233 if(result.isEmpty()) {234 return null;235 }236 return JSON.toJSONString(result, SerializerFeature.WriteMapNullValue);237 }238 239 /**240 * 基本的检查241 * @param oldObj242 * @param newObj243 * @param selective244 * @param array245 * @return246 */247 @SuppressWarnings("unchecked")248 private static String baseCompare(Object oldObj, Object newObj, boolean selective, CompareCollectionDto ... array) {249 if(null==array || array.length == 0) {250 throw new RuntimeException("集合对象的数组不能为空");251 }252 List result = getDiffernceList(oldObj, newObj, false, selective);253 for(CompareCollectionDto dto : array) {254 List cr = null;255 if(selective) {256 cr = compareCollectionListSelective(dto.getOldCollection(), dto.getNewCollection());257 } else {258 cr = compareCollectionList(dto.getOldCollection(), dto.getNewCollection());259 }260 261 if(!cr.isEmpty()) {262 DifferenceWapper wapper = DifferenceWapper.builder().column(dto.getFiledName())263 .difference(Difference.builder().newValue(cr).build()).build();264 result.add(wapper);265 }266 }267 if(!result.isEmpty()) {268 return JSON.toJSONString(result, SerializerFeature.WriteMapNullValue);269 }270 return null;271 }272 273 /**274 * 275 * @param oldObj276 * @param newObj277 * @param onlyCheck 是否仅校验(遇到不同则立刻返回)278 * @param selective true代表如果newObj的属性的值为null则跳过该属性比较279 * @return280 */281 private static List getDiffernceList(Object oldObj, Object newObj, boolean onlyCheck, boolean selective) {282 List result = new ArrayList<>();283 284 // 是否是创建类型的比较285 boolean isCreate = false;286 if(oldObj == null) {287 isCreate = true;288 } else {289 if(null == newObj) {290 throw new RuntimeException("更改后的对象不能为null");291 }292 }293 if(!isCreate && !newObj.getClass().equals(oldObj.getClass())) {294 throw new RuntimeException("两个需要比较的对象必须为同一类型");295 }296 // 同一个对象就没必要对比了297 if(oldObj == newObj) {298 return result;299 }300 301 Set fields = getField(newObj.getClass());302 Set methods = getMethod(newObj.getClass());303 304 if(null != fields && !fields.isEmpty()) {305 String methodName = null;306 String fieldName = null;307 for(Field field : fields) {308 if(ignoreFileds.contains(field.getName())) {309 continue;310 }311 fieldName = field.getName();312 methodName = "get".concat(fieldName.substring(0, 1).toUpperCase()).concat(fieldName.substring(1));313 314 Method method = filterMethod(methods, methodName);315 if(null == method) {316 // 是小boolean类型则需要特殊处理317 if("boolean".equals(field.getType().getTypeName())) {318 if(fieldName.startsWith("is")) {319 // 如果第三位是大写320 boolean upper = isUpperString(fieldName.substring(2, 3));321 if(upper) {322 methodName = fieldName;323 } else {324 methodName = "is".concat(fieldName.substring(0, 1).toUpperCase()).concat(fieldName.substring(1));325 }326 } else {327 methodName = "is".concat(fieldName.substring(0, 1).toUpperCase()).concat(fieldName.substring(1));328 }329 method = filterMethod(methods, methodName);330 }331 if(null == method) {332 continue;333 }334 }335 try {336 Object oldValue = null;337 if(!isCreate) {338 oldValue = method.invoke(oldObj);339 }340 Object newValue = method.invoke(newObj);341 if(null == oldValue) {342 if(null != newValue) {343 result.add(DifferenceWapper.builder().column(fieldName).difference(Difference.builder().newValue(newValue).oldValue(oldValue).build()).build());344 }345 } else {346 if(null != newValue) {347 // 处理0.0和0的不同348 if(newValue instanceof BigDecimal){349 BigDecimal newValue1 = (BigDecimal) newValue;350 BigDecimal oldValue1 = (BigDecimal) oldValue;351 if(newValue1.compareTo(oldValue1) != 0){352 result.add(DifferenceWapper.builder().column(fieldName).difference(Difference.builder().newValue(newValue).oldValue(oldValue).build()).build());353 }354 } else if(!oldValue.equals(newValue)) {355 result.add(DifferenceWapper.builder().column(fieldName).difference(Difference.builder().newValue(newValue).oldValue(oldValue).build()).build());356 }357 } else {358 if(!selective) {359 result.add(DifferenceWapper.builder().column(fieldName).difference(Difference.builder().newValue(newValue).oldValue(oldValue).build()).build());360 }361 }362 }363 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {364 log.error("反射调用get方法异常", e);365 throw new RuntimeException(e);366 }367 /**368 * 如果仅仅校验是否不同,则提前返回369 */370 if(onlyCheck && !result.isEmpty()) {371 return result;372 }373 }374 }375 return result;376 }377 378 /**379 * 获取类的所有字段380 * @param type381 * @return382 */383 @SuppressWarnings("unchecked")384 private static Set getField(Class type) {385 String className = type.getName();386 if(fieldCache.get(className) == null || fieldCache.get(className).isEmpty()) {387 synchronized (className) {388 if(fieldCache.get(className) == null || fieldCache.get(className).isEmpty()) {389 fieldCache.put(className, ReflectionUtils.getAllFields(type));390 }391 }392 }393 return fieldCache.get(className);394 }395 396 /**397 * 获取类的所有方法398 * @param type399 * @return400 */401 @SuppressWarnings("unchecked")402 private static Set getMethod(Class type) {403 String className = type.getName();404 if(methodCache.get(className) == null || methodCache.get(className).isEmpty()) {405 synchronized (className) {406 if(methodCache.get(className) == null || methodCache.get(className).isEmpty()) {407 methodCache.put(className, ReflectionUtils.getAllMethods(type));408 }409 }410 }411 return methodCache.get(className);412 }413 414 /**415 * 比较2个字符串集合的差异(只支持基本类型)416 * @param 417 * @param oldCollection418 * @param newCollection419 * @return420 */421 private static List compareCollectionList(Collection oldCollection, Collection newCollection) {422 String flag = UPDATE;423 if(null==oldCollection || oldCollection.isEmpty()) {424 if(null==newCollection || newCollection.isEmpty()) {425 throw new RuntimeException("两个需要比较的集合不能都为空");426 }427 flag = ADD;428 } else {429 if(null==newCollection || newCollection.isEmpty()) {430 flag = DELETE;431 }432 }433 434 if(ADD.equals(flag)) {435 return compareCollectionAdd(newCollection);436 } else if(DELETE.equals(flag)) {437 return compareCollectionDelete(oldCollection);438 } else {439 return compareCollectionUpdate(oldCollection, newCollection);440 }441 }442 443 /**444 * 比较2个字符串集合的差异(只支持基本类型),如果newCollection为null,则忽略比较445 * @param 446 * @param oldCollection447 * @param newCollection448 * @return449 */450 private static List compareCollectionListSelective(Collection oldCollection, Collection newCollection) {451 String flag = UPDATE;452 if(null==oldCollection || oldCollection.isEmpty()) {453 if(null==newCollection || newCollection.isEmpty()) {454 return new ArrayList<>();455 }456 flag = ADD;457 } else {458 if(null==newCollection || newCollection.isEmpty()) {459 return new ArrayList<>();460 }461 }462 463 if(ADD.equals(flag)) {464 return compareCollectionAdd(newCollection);465 } else if(DELETE.equals(flag)) {466 return compareCollectionDelete(oldCollection);467 } else {468 return compareCollectionUpdate(oldCollection, newCollection);469 }470 }471 472 /**473 * 新增474 * @param newCollection475 * @return476 */477 private static List compareCollectionAdd(Collection newCollection) {478 List result = new ArrayList<>(1);479 DifferenceWapper diff = new DifferenceWapper();480 Difference difference = new Difference();481 difference.setNewValue(newCollection);482 diff.setColumn("add");483 diff.setDifference(difference);484 result.add(diff);485 return result;486 }487 488 /**489 * 删除490 * @param oldCollection491 * @return492 */493 private static List compareCollectionDelete(Collection oldCollection) {494 List result = new ArrayList<>(1);495 DifferenceWapper diff = new DifferenceWapper();496 Difference difference = new Difference();497 difference.setOldValue(oldCollection);498 diff.setColumn("delete");499 diff.setDifference(difference);500 result.add(diff);501 return result;502 }503 504 /**505 * 更新506 * @param oldCollection507 * @param newCollection508 * @return509 */510 private static List compareCollectionUpdate(Collection oldCollection, Collection newCollection) {511 List ret = new ArrayList<>();512 513 Set result = new HashSet<>();514 Set oldSet = new HashSet<>();515 oldSet.addAll(oldCollection);516 Set newSet = new HashSet<>();517 newSet.addAll(newCollection);518 519 //取交集520 result.addAll(oldCollection);521 result.retainAll(newCollection);522 523 //没有变更524 if(result.size()==newSet.size() && result.size()==oldSet.size()) {525 return ret;526 }527 528 //被删除的529 //取差集530 oldSet.removeAll(result);531 532 //新增的533 //取差集534 newSet.removeAll(result);535 536 if(!oldSet.isEmpty()) {537 DifferenceWapper diff = new DifferenceWapper();538 Difference difference = new Difference();539 difference.setOldValue(oldSet);540 diff.setColumn("delete");541 diff.setDifference(difference);542 ret.add(diff);543 }544 if(!newSet.isEmpty()) {545 DifferenceWapper diff = new DifferenceWapper();546 Difference difference = new Difference();547 difference.setNewValue(newSet);548 diff.setColumn("add");549 diff.setDifference(difference);550 ret.add(diff);551 }552 553 return ret;554 }555 556 /**557 * 过滤方法558 * @param methods559 * @param methodName560 * @return561 */562 private static Method filterMethod(Set methods, String methodName) {563 if(null != methods && !methods.isEmpty()) {564 for(Method method : methods) {565 if(method.getName().equals(methodName)) {566 return method;567 }568 }569 }570 return null;571 }572 573 /**574 * 匹配是否是大写字母575 * @param str576 * @return577 */578 private static boolean isUpperString(String str) {579 Pattern p = Pattern.compile("[A-Z]");580 Matcher matcher = p.matcher(str);581 return matcher.matches();582 }583 584 /**585 * 聚合主和明细数据的变更记录586 * @param main587 * @param attrName 显示在主对象里的属性588 * @param detail 明细对象的属性差异的list589 */590 private static List aggregateData(List main, String attrName, List detail) {591 if(null != detail && !detail.isEmpty()) {592 if(null == main || main.isEmpty()) {593 main = new ArrayList<>(1);594 }595 main.add(DifferenceWapper.builder().column(attrName).difference(Difference.builder().newValue(detail).build()).build());596 }597 return main;598 }599 600 /**601 * 聚合主和明细数据的变更记录并转换成json602 * @param main603 * @param attrName 显示在主对象里的属性604 * @param detail 明细对象的属性差异的list605 */606 private static String aggregateDataAndConvertToJson(List main, String attrName, List detail) {607 List result = aggregateData(main, attrName, detail);608 if(null!=result && !result.isEmpty()) {609 return JSON.toJSONString(result, SerializerFeature.WriteMapNullValue);610 }611 return null;612 }613 614 @Getter615 @Setter616 @Builder617 @NoArgsConstructor618 @AllArgsConstructor619 static class DifferenceWapper implements Serializable {620 private static final long serialVersionUID = -3369182406683473741L;621 private String column;622 private Difference difference;623 }624 625 @Getter626 @Setter627 @Builder628 @NoArgsConstructor629 @AllArgsConstructor630 static class Difference implements Serializable{631 private static final long serialVersionUID = 2478199484126795290L;632 633 private Object oldValue;634 private Object newValue;635 }636 637 }
辅助DTO类:
1 package com.longge.utils; 2 3 import java.io.Serializable; 4 import java.util.Collection; 5 6 import lombok.AllArgsConstructor; 7 import lombok.Builder; 8 import lombok.Getter; 9 import lombok.NoArgsConstructor;10 import lombok.Setter;11 12 /**13 * 集合类型的属性的比较包装类14 * @author yangzhilong15 *16 * @param17 */18 @Getter19 @Setter20 @Builder21 @NoArgsConstructor22 @AllArgsConstructor23 public class CompareCollectionDto implements Serializable {24 private static final long serialVersionUID = -4977550220859897197L;25 private String filedName;26 private Collection oldCollection;27 private Collection newCollection;28 }
测试类:
1 package com.tomato.boss.common; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import com.tomato.boss.common.utils.CompareCollectionDto; 7 import com.tomato.boss.common.utils.CompareDifferenceUtil; 8 9 import lombok.Getter;10 import lombok.Setter;11 12 public class CompareDifferenceUtilTest {13 public static void main(String[] args) {14 Dto dto1 = new Dto();15 Dto dto2 = new Dto();16 17 dto1.setAge(10);18 dto1.setDelete(false);19 dto1.setFlag(false);20 dto1.setIssTrue(false);21 dto1.setName("张三");22 dto1.setSize(10);23 dto1.setTrue(false);24 dto1.setCreateBy("xxx");25 dto1.setType("1");26 27 dto2.setAge(15);28 dto2.setDelete(true);29 dto2.setFlag(true);30 dto2.setIssTrue(true);31 dto2.setName("李四");32 dto2.setSize(20);33 dto2.setTrue(true);34 dto2.setCreateBy("yyyyy");35 dto2.setType("2");36 37 ListoldList = Arrays.asList(1,2,3);38 List newList = Arrays.asList(2,3,4);39 40 dto1.setArr(oldList);41 dto2.setArr(newList);42 43 System.out.println(CompareDifferenceUtil.isDifference(dto1, dto2));44 System.out.println(CompareDifferenceUtil.compare(dto1, dto2));45 System.out.println(CompareDifferenceUtil.isDifference(oldList, newList));46 System.out.println(CompareDifferenceUtil.compareCollection(oldList, newList));47 48 CompareCollectionDto dto = new CompareCollectionDto<>("arr",oldList,newList);49 CompareCollectionDto dtox = new CompareCollectionDto<>("arry",oldList,newList);50 51 System.out.println(CompareDifferenceUtil.compare(dto1, dto2, dto, dtox));52 53 Children c1 = new Children();54 c1.setCname("张三");55 56 Children c2 = new Children();57 c2.setCname("李四");58 59 System.out.println(CompareDifferenceUtil.compareMainAndDetail(dto1, dto2, "child", c1, c2));60 }61 62 63 @Getter64 @Setter65 public static class Dto extends P{66 private boolean isTrue;67 private boolean issTrue;68 private boolean delete;69 private Boolean flag;70 private String name;71 private Integer age;72 private int size;73 private String createBy;74 private List arr;75 }76 77 @Getter78 @Setter79 public static class P {80 private String type;81 }82 83 @Getter84 @Setter85 public static class Children {86 private String cname;87 }88 }
测试结果比较对象的(JSON格式的字符串):
[{ "column": "name", "difference": { "newValue": "李四", "oldValue": "张三" } }, { "column": "delete", "difference": { "newValue": true, "oldValue": false } }, { "column": "age", "difference": { "newValue": 15, "oldValue": 10 } }, { "column": "issTrue", "difference": { "newValue": true, "oldValue": false } }, { "column": "isTrue", "difference": { "newValue": true, "oldValue": false } }, { "column": "flag", "difference": { "newValue": true, "oldValue": false } }, { "column": "size", "difference": { "newValue": 20, "oldValue": 10 } }]
比较集合的:
1 [{ 2 "column": "delete", 3 "difference": { 4 "newValue": null, 5 "oldValue": 1 6 } 7 }, { 8 "column": "add", 9 "difference": {10 "newValue": 4,11 "oldValue": null12 }13 }14 ]
比较dto+集合的:
1 [{ 2 "column": "name", 3 "difference": { 4 "newValue": "李四", 5 "oldValue": "张三" 6 } 7 }, { 8 "column": "arr", 9 "difference": {10 "newValue": [{11 "column": "delete",12 "difference": {13 "newValue": null,14 "oldValue": [1]15 }16 }, {17 "column": "add",18 "difference": {19 "newValue": [4],20 "oldValue": null21 }22 }23 ],24 "oldValue": null25 }26 }27 ]
比较dto+子dto的:
1 [ 2 { 3 "column": "name", 4 "difference": { 5 "newValue": "李四", 6 "oldValue": "张三" 7 } 8 }, { 9 "column": "delete",10 "difference": {11 "newValue": true,12 "oldValue": false13 }14 }, {15 "column": "child",16 "difference": {17 "newValue": [{18 "column": "cname",19 "difference": {20 "newValue": "李四",21 "oldValue": "张三"22 }23 }24 ],25 "oldValue": null26 }27 }28 ]