You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 05:36:00