Javers - 在单个查询中跟踪子对象对父对象的更改

时间:2016-10-03 07:07:22

标签: java javers

我使用https://github.com/javers/javers/issues/98中描述的类似用例。

在下面的代码中,当我更新依赖对象并提交Employee bob时,我希望依赖的更改从JQL查询中检索QueryBuilder.byInstanceId(1L, Employee.class)

//update dependent's name ( WANT TO TRACK THIS FROM ROOT EMPLOYEE OBJECT)
Dependent aDep = bob.getDependentById(1L);
aDep.setName("Ramsey");
javers.commit("hr.manager", bob);

完整代码:

import lombok.Getter;
import lombok.Setter;
import org.javers.core.Javers;
import org.javers.core.JaversBuilder;
import org.javers.core.changelog.SimpleTextChangeLog;
import org.javers.core.diff.Change;
import org.javers.repository.jql.QueryBuilder;
import org.junit.Test;

import javax.persistence.Id;
import java.util.*;

public class JaversTests {

    @Test
    public void shouldPrintTextChangeLog() {
        // given:
        Javers javers = JaversBuilder.javers().build();

        //initial employee
        Employee bob = new Employee(1L, "Bob", 9_000, "Junior Developer");
        javers.commit("hr.manager", bob);

        // promoted
        bob.setPosition("Senior Developer");
        bob.setSalary(11_000);
        javers.commit("hr.director", bob);

        //add dependents
        bob.addDependents(new Dependent(1L, "Ram", "Son"), new Dependent(2L, "Kabita", "Daughter"));
        javers.commit("hr.manager", bob);

        //update dependent's name ( WANT TO TRACK THIS FROM ROOT EMPLOYEE OBJECT)
        Dependent aDep = bob.getDependentById(1L);
        aDep.setName("Ramsey");
        javers.commit("hr.manager", bob);

        List<Change> employeeChanges = javers.findChanges(
            QueryBuilder.byInstanceId(1L, Employee.class).withChildValueObjects().build());

        String employeeChangeLog = javers.processChangeList(employeeChanges, new SimpleTextChangeLog());

        System.out.println(employeeChangeLog);
    }
}

@Getter
@Setter
class Employee {
    @Id
    Long id;
    String name;
    double salary;
    String position;
    Set<Dependent> dependents = new HashSet<>();

    public Employee(Long id, String name, double salary, String position) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.position = position;
    }

    void addDependents(Dependent... dependents) {
        getDependents().addAll(new LinkedList(Arrays.asList(dependents)));
    }

    Dependent getDependentById(Long id) {
        for (Dependent dep : getDependents()) {
            if (dep.getId() == id) {
                return dep;
            }
        }
        return null;
    }
}

@Setter
@Getter
class Dependent {

    @Id
    Long id;
    String name;
    String relation;

    public Dependent(Long id, String name, String relation) {
        this.id = id;
        this.name = name;
        this.relation = relation;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Dependent dependent = (Dependent) o;
        return Objects.equals(id, dependent.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

我得到了以下输出。这缺少了依赖名称的变化(Ram to Ramsey)。

commit 3.0, author: hr.manager, Oct 3, 2016 12:45:57 PM
  changed object: us.sdata.enroll.Employee/1
    set changed on 'dependents' property: [added:'us.sdata.enroll.Dependent/2', added:'us.sdata.enroll.Dependent/1']
commit 2.0, author: hr.director, Oct 3, 2016 12:45:57 PM
  changed object: us.sdata.enroll.Employee/1
    value changed on 'salary' property: '9000.0' -> '11000.0'
    value changed on 'position' property: 'Junior Developer' -> 'Senior Developer'

谢谢!

2 个答案:

答案 0 :(得分:1)

withChildValueObjects()过滤器仅适用于子(依赖)ValueObjects和 你有两个类映射为实体(@Id ann存在)。

由于EmployeeDependent对象之间没有明确的父子关系,因此JaVers会平等对待它们。 如果将Dependent映射为ValueObject(删除@Id就足够了),您的测试将通过。

答案 1 :(得分:1)

在这种情况下没有干净的解决方案,因为JaVers JQL不支持实体连接。因此,没有简单的方法来查找实体X的快照,这些快照是从实体Y的快照引用的。 我可以建议的是准备以DDD聚合(Entity及其子ValueObjects)形式塑造的DTO对象,并将这些DTO提交给JaVers而不是原始域对象。我知道这不是一个多变的优雅解决方案。