如何用JaVers创建仅含差异字段的POJO实例?
问题描述
我可能完全误解了JaVers的用途,正为此头疼。我有个带嵌套子类的POJO,想创建一个新实例——只保留另外两个实例的差异字段,所有匹配的字段都设为null。下面是示例代码,请问用JaVers能实现吗?也可以推荐其他工具。
public class RootClass { private String someString; private String someOtherString; private ANestedClass aNestedClass; public RootClass(String someString, String someOtherString, ANestedClass aNestedClass) { this.someString = someString; this.someOtherString = someOtherString; this.aNestedClass = aNestedClass; } public static class ANestedClass { private String someNestedString; private String someOtherNestedString; public ANestedClass(String someNestedString, String someOtherNestedString) { this.someNestedString = someNestedString; this.someOtherNestedString = someOtherNestedString; } } } public RootClass getDifferenceObject() { RootClass a = new RootClass("foo", "bar", new ANestedClass("hello", "world")); RootClass b = new RootClass("foo", "blabla", new ANestedClass("hello", "earth")); Javers javers = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.LEVENSHTEIN_DISTANCE).build(); Diff compare = javers.compare(a, b); // now convert this somehow RootClass abDiff = ...; assert(abDiff.someString == null); assert(abDiff.someOtherString.equals("blabla")); assert(abDiff.aNestedClass.someNestedString == null); assert(abDiff.aNestedClass.someOtherNestedString.equals("earth")); }
解决方案
JaVers完全能实现这个需求,核心思路是解析Diff对象里的变更记录,逐个字段构建差异实例。具体步骤如下:
- 从JaVers的对比结果
Diff中提取所有字段变更,区分顶层字段和嵌套字段的变化 - 递归创建差异对象:不管是顶层类还是嵌套类,只给有差异的字段赋值(取实例b的新值),无差异的字段直接设为null
- 处理嵌套对象时,要确保即使嵌套类只有部分字段变更,也能正确实例化并设置对应值
具体代码实现
import org.javers.core.Javers; import org.javers.core.JaversBuilder; import org.javers.core.diff.Diff; import org.javers.core.diff.changetype.ValueChange; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class DifferenceBuilder { public static <T> T buildDiffObject(T a, T b, Javers javers) throws Exception { Diff diff = javers.compare(a, b); Map<String, Object> fieldValues = new HashMap<>(); // 遍历所有值变更记录 for (ValueChange change : diff.getChangesByType(ValueChange.class)) { String propertyPath = change.getPropertyName(); // 处理嵌套字段路径,比如aNestedClass.someOtherNestedString if (propertyPath.contains(".")) { String[] parts = propertyPath.split("\\.", 2); String nestedFieldName = parts[0]; String nestedProperty = parts[1]; // 递归构建嵌套对象的差异实例 Object nestedA = getFieldValue(a, nestedFieldName); Object nestedB = getFieldValue(b, nestedFieldName); Object nestedDiff = buildDiffObject(nestedA, nestedB, javers); fieldValues.put(nestedFieldName, nestedDiff); } else { // 顶层字段直接取b的新值 fieldValues.put(propertyPath, change.getRight()); } } // 通过反射创建目标实例,无差异字段设为null Constructor<?> constructor = a.getClass().getDeclaredConstructors()[0]; Class<?>[] paramTypes = constructor.getParameterTypes(); Object[] params = new Object[paramTypes.length]; Field[] fields = a.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { fields[i].setAccessible(true); params[i] = fieldValues.getOrDefault(fields[i].getName(), null); } constructor.setAccessible(true); return (T) constructor.newInstance(params); } private static Object getFieldValue(Object obj, String fieldName) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); return field.get(obj); } // 测试运行 public static void main(String[] args) throws Exception { RootClass a = new RootClass("foo", "bar", new RootClass.ANestedClass("hello", "world")); RootClass b = new RootClass("foo", "blabla", new RootClass.ANestedClass("hello", "earth")); Javers javers = JaversBuilder.javers().build(); RootClass abDiff = buildDiffObject(a, b, javers); assert abDiff.someString == null; assert abDiff.someOtherString.equals("blabla"); assert abDiff.aNestedClass.someNestedString == null; assert abDiff.aNestedClass.someOtherNestedString.equals("earth"); System.out.println("所有断言通过,差异对象构建成功"); } }
补充说明
- 这段代码用反射实例化对象,要求POJO的构造器参数顺序和字段声明顺序一致(示例代码是匹配的),如果你的类构造器参数顺序不一样,需要调整反射逻辑来对应参数。
- 对于更复杂的多层嵌套结构,这个递归逻辑也能适配,JaVers会自动识别所有层级的字段差异。
- 如果不想用JaVers,也可以试试Apache Commons BeanUtils或者ModelMapper,但这些工具需要你自己实现字段比较逻辑,JaVers的优势是能自动处理嵌套、集合等复杂结构的差异对比。
内容的提问来源于stack exchange,提问作者Selbi
相关产品推荐
相关产品推荐

