Search code examples
javajavers

JaVers not detecting changes to object attributes inside a collection


I would like to use JaVers to track changes of my Java objects. The basic examples are working fine, but I can't get it to detect changes on objects stored in a collection.

If I extend the ChangeLogExample.class example to change e.g. one of the sub-ordinates:

public static void main(String[] args) {

    Javers javers = JaversBuilder.javers().build();
    Employee bob = new Employee("Bob", 9_000, "Scrum master" );
    javers.commit("hr.manager", bob);

    // do some changes and commit
    bob.setPosition("Team Lead");
    bob.setSalary(11_000);
    javers.commit("hr.director", bob);

    bob.addSubordinates(new Employee("Trainee One"), new Employee("Trainee Two"));
    javers.commit("hr.manager", bob);

    bob.getSubordinates().get(0).setAge(42); // <<<< This is the change I want to detect
    javers.commit("hr.manager", bob); 

    // when:
    List<Change> changes = javers.findChanges(
        QueryBuilder.byInstanceId("Bob", Employee.class).withChildValueObjects().build());
    String changeLog = javers.processChangeList(changes, new SimpleTextChangeLog());

    // then:
    System.out.println(changeLog);
}

This is the changelog that is printed:

commit 3.0, author: hr.manager, 2017-06-06 11:17:17
  changed object: Employee/Bob
    list changed on 'subordinates' property: [(0).added:'Employee/Trainee One', (1).added:'Employee/Trainee Two']
commit 2.0, author: hr.director, 2017-06-06 11:17:17
  changed object: Employee/Bob
    value changed on 'position' property: 'Scrum master' -> 'Team Lead'
    value changed on 'salary' property: '9000' -> '11000'

So the change to the age of the first subordinate doesn't show up in the changelog.

Using withChildValueObjects() does not make a difference.

I get the change to the trainee's age when I commit the change to the Employee instance separately, but that is not want I expected (nor what I want).

So my question is: how to get the change to the age show up in the ChangeLog?


I am using JaVers 3.2.0

The Employee class is unchanged from the JaVers examples: https://github.com/javers/javers/tree/master/javers-core/src/test/java/org/javers/core/examples/model

The main() method is simply the Test from https://github.com/javers/javers/blob/master/javers-core/src/test/java/org/javers/core/examples/ChangeLogExample.java


Solution

  • Ok, a few issues here. First of all, Empolyee objects are mapped as Entities. So in JaVers, there is no parent/child relationship between them (in fact any kind of relationship). That's why withChildValueObjects() filter doesn't apply here. It works only for ValueObjects owned by Entities, see http://javers.org/documentation/jql-examples/#child-value-objects-filter

    Still, there are two ways to improve your query.

    1. Ask directly for the Entity instance you want to trace.

    2. Use the new Shadow API with Query Scopes, see http://javers.org/documentation/jql-examples/#query-for-shadows It's a new feature and will be improved in the feature. You can use it already if snapshots of both Entities will be selected by the query.

    See code below:

    def "should ... "(){
      given:
      Javers javers = JaversBuilder.javers().build()
      Employee bob = new Employee("Bob", 9_000, "Scrum master" )
      javers.commit("hr.manager", bob)
    
      // do some changes and commit
      bob.setPosition("Team Lead")
      bob.setSalary(11_000)
      javers.commit("hr.director", bob)
    
      bob.addSubordinates(new Employee("Trainee One"), new Employee("Trainee Two"))
      javers.commit("hr.manager", bob)
    
      bob.getSubordinates().get(0).setAge(42) // <<<< This is the change I want to detect
      bob.salary++ // !
      javers.commit("hr.manager", bob)
    
      when: "ask for the the right Entity instance"
      List changes = javers.findChanges(
              QueryBuilder.byInstanceId("Trainee One", Employee.class).build())
    
      then:
      println( javers.processChangeList(changes, new SimpleTextChangeLog()) )
      true
    
      when: "use the new Shadows"
      List shadows = javers.findShadows(
              QueryBuilder.byInstanceId("Bob", Employee.class)
                          .withShadowScopeDeep().build())
    
      then:
      shadows.each {
          assert it.get() instanceof Employee
      }
      Employee lastBobShadow = shadows[0].get()
      assert shadows[0].commitMetadata.id.majorId == 4
      assert lastBobShadow.subordinates[0].name == "Trainee One"
      assert lastBobShadow.subordinates[0].age == 42
    }