Search code examples
javajavers

Can Javers compare two Sets containing valueObjects identifying them with the equals/hashcode methods to detect changes?


Can Javers compare two sets to identify the changes happened in the objects in the new set for each corresponding object in the old set?

Does the implementation rely on the equals method of the value object class? I implemented equals method for my value object, but it still seems to treat them as different objects. If it is not using the equals method, can we do that? Because, Java Set itself relies on the equals method to make sure no duplicates exist in the set.

Please suggest.

In the below test case, I'm using sysCode and localId as a combined identifier for the object, not including the goldenId. But the diff (output below the class) is not recognizing that equal objects and showing the diff as all the objects in the old set as removed and the objects in the new se as added, which doesn't seem to add any value.

Here is my test case:

Javers javers = JaversBuilder.javers().build();
SetConMap oldMap = new SetConMap();
Set<Contact> oldSysCons = new HashSet<>();
oldMap.setSysCons(oldSysCons);
oldSysCons.add(new Contact().sysCode("AL").localId("123").goldenId("ABC"));
oldSysCons.add(new Contact().sysCode("AL").localId("456").goldenId("ABC"));

SetConMap newMap = new SetConMap();
Set<Contact> newSysCons = new HashSet<>();
newMap.setSysCons(newSysCons);
newSysCons.add(new Contact().sysCode("AL").localId("123").goldenId("DEF"));
newSysCons.add(new Contact().sysCode("AL").localId("456").goldenId("DEF"));
newSysCons.add(new Contact().sysCode("AL").localId("789").goldenId("DEF"));

Diff diff = javers.compare(oldMap, newMap);
System.out.println(diff);
private static class Contact {
    private String sysCode;
    private String localId;
    private String goldenId;
//getters, setters.
    @Override
    public int hashCode() {
        return super.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return obj != null && (obj instanceof Contact) && Objects.equals(sysCode, ((Contact) obj).getSysCode())
            && Objects.equals(localId, ((Contact) obj).getLocalId());
    }
}

Diff:

* new object: org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/c76fadcd55d4dbf6d8cbe49310b1ab26
* new object: org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/96b23b34a0f42a5105491350db5c46f1
* new object: org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/a9c7f46bbe5f0e46b5751f3f5c5d8542
* object removed: org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/a53e4ba1b22c4f290a80daf40adf5d81
* object removed: org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/782edf187b8add9bab004ca06f7b6c7c
* changes on org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/ :
  - 'sysCons' collection changes :
    . 'org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/782edf187b8add9bab004ca06f7b6c7c' removed
    . 'org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/a53e4ba1b22c4f290a80daf40adf5d81' removed
    . 'org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/a9c7f46bbe5f0e46b5751f3f5c5d8542' added
    . 'org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/c76fadcd55d4dbf6d8cbe49310b1ab26' added
    . 'org.ishafoundation.cdi.web.rest.GoldenContactResource$SetConMap/#sysCons/96b23b34a0f42a5105491350db5c46f1' added

Solution

  • The Contact class is considered as a Value Object by Javers: in this case, it performs a property by property comparison (see here).

    To get more detailed comparison results, you may transform the Contact class into an Entity using the @Id annotation on one of its properties.

    Another solution may be to write a custom comparator here (but I never tried it).