Search code examples
javacasjavers

Javers is not reporting references changes in object hierarchy


Problem Statement

I am experimenting with Javers 3.5.1's ability to report back changes that are committed. It appears that changes are only reported if they are primitives; when an object reference changes in the hierarchy of an entity, the changes is not reported back.

Sample

Here is a sample:

    Javers javers = JaversBuilder.javers().build();

    RegexRegisteredService svc2 = new RegexRegisteredService();
    // Set a reference to something
    svc2.setUsernameAttributeProvider(new DefaultRegisteredServiceUsernameProvider());
    svc2.setId(345);
    javers.commit("345", svc2);

    // change the reference
    svc2.setUsernameAttributeProvider(new AnonymousRegisteredServiceUsernameAttributeProvider());
    javers.commit("345", svc2);

    List<Change> changes = javers.findChanges(QueryBuilder.byInstanceId("345", 
                               RegexRegisteredService.class).build());
    System.out.println(changes.size());

The outcome of the above code snippet is 0.

Diagnosis

Further diagnostics show that object references in the containing classes get their own id; the findChanges() function only looks at the containing class/entity with that id, and does not traverse down the hierarchy to locate other references and properties. When in the InMemoryRepository begins to compare snapshots from history to the globalId that is passed, it will only look at the parent globalId and will not go down the list of contained classes:

    Iterator it = this.getAll().iterator();

    while(it.hasNext()) {
        CdoSnapshot snapshot = (CdoSnapshot) it.next();
        if (snapshot.getGlobalId().equals(globalId)) {
            filtered.add(snapshot);
        }
    ...

Here, the globalId that is passed is org.apereo.cas.services.RegexRegisteredService/345. However, nothing in this id has changed; what has really changed is mapped to org.apereo.cas.services.RegexRegisteredService/345#usernameAttributeProvider which will never be retrieved because the above check fails the equality condition.

The hierarchy is as such:

public interface RegisteredService {}

public abstract class AbstractService implements RegisteredService {
   // with setters and getters
   private UsernameAttributeProviderInterface usernameAttributeProvider;
}

public class RegexRegisteredService extends AbstractService {}

Alternative?

How about something like this instead?

    Iterator it = this.getAll().iterator();

    while(it.hasNext()) {
        CdoSnapshot snapshot = (CdoSnapshot) it.next();
        if (snapshot.getGlobalId().value().startsWith(globalId.value())) {
            filtered.add(snapshot);
        }
    ...

Is this functionality absent in Javers, or have I missed something? Wondering if I should perhaps design my own repository class?


Solution

  • Turns out that I needed to do something quite simple:

    final List<Change> changes = javers.findChanges(
                QueryBuilder.byInstanceId("345, RegexRegisteredService.class)
                        .withChildValueObjects()
                        .build());
    

    Reference: https://javers.org/documentation/jql-examples/#child-value-objects-filter