New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lists: newObject and ValueChange? #129

Closed
AzraelCole opened this Issue Mar 18, 2015 · 10 comments

Comments

Projects
None yet
3 participants
@AzraelCole

AzraelCole commented Mar 18, 2015

Hi,

I have a problem with changes that are made on objects within a list.
Assumption: There is an object with a list of other objects. Then an element of the list is removed and later two new elements are added.
Now a ValueChange is fired and a NewObject.
The ValueChange gives me the differences (prop1value -> prop2value), but the NewObject only says "hello, I am new".

For a consistent presentation I would like to have the information about the NewObject more detailed (like null -> prop1value).
Here is a small example:

import java.util.ArrayList;
import java.util.List;

import org.javers.core.Javers;
import org.javers.core.JaversBuilder;
import org.javers.core.diff.Diff;
import org.javers.core.diff.changetype.NewObject;
import org.javers.core.diff.changetype.ValueChange;
import org.javers.core.metamodel.annotation.DiffIgnore;

public class Main {

    public static void main(String[] args) {

        // old version, usually fetched from database
        Person person1 = new Person("Peter");
        person1.add(new Pet("cat", 1));

        // new version
        Person person2 = new Person("Peter");
        person2.add(new Pet("dog", 2));
        person2.add(new Pet("mouse", 3));

        Javers javers = JaversBuilder.javers().build();
        Diff diff = javers.compare(person1, person2);

        List<ValueChange> changesByType = diff.getChangesByType(ValueChange.class);
        for (ValueChange valueChange : changesByType) {
            System.out.println(valueChange);
        }

        List<NewObject> newObjects = diff.getChangesByType(NewObject.class);
        for (NewObject newObject : newObjects) {
            System.out.println(newObject);
        }

    }
}

class Person {

    private String name;
    private List<Pet> pets = new ArrayList<>();

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public List<Pet> getPets() {
        return pets;
    }

    public void add(Pet pet) {
        pets.add(pet);
    }
}

class Pet {

    private String name;
    private int age;

    @DiffIgnore
    private int id;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
        id = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

This produces the output

ValueChange{globalId:'Person/#pets/0', property:'name', oldVal:'cat', newVal:'dog'}
ValueChange{globalId:'Person/#pets/0', property:'age', oldVal:'1', newVal:'2'}
NewObject{globalId:'Person/#pets/1'}

But for the NewObject I have no idea what the values are for the properties (of course here I am only interesed in the properties that are not annotated with @DiffIgnore).

I have to show the changes in a consisten way later in my webapp.

How can I handle that?
Or would it be possible to detect that "cat" is removed (-> ObjectRemoved) and "dog" and "mouse" are added (-> NewObject)?

Thanks!

@pszymczyk

This comment has been minimized.

Member

pszymczyk commented Mar 18, 2015

Hi

For the second question:
There is possible to detect that cat was removed, your Pet.class has field id so probably it is Entity, I sugesst to put the Id annotation over that filed, as below:

public class Pet {

    private String name;
    private int age;

    @Id
    private int id;
...

then Javers can correctly recognize this element as list of entities and compares as you expect:

NewObject{globalId:'org.javers.Pet/2'}
NewObject{globalId:'org.javers.Pet/3'}
ObjectRemoved{globalId:'org.javers.Pet/1'}

You can find more information about Javers Entities and Entitie id in documentation:

  1. http://javers.org/documentation/domain-configuration/#domain-model-mapping - there are few words about concept of Entieties, ValueObject and Value
  2. http://javers.org/documentation/domain-configuration/#entity-id-property - here you can read about supported @id annotations, annotation levels etc

If you are interested in comparing lists I also suggest you to read about list compare algorithms:
http://javers.org/documentation/diff-configuration/#list-algorithms. Javers privides two algorithms Simple and Levenstein, there are few difference between them, Simple is used by default.

I hope that documentation will be helpfull :), now I am considering about your first question, write back soon

@pszymczyk

This comment has been minimized.

Member

pszymczyk commented Mar 18, 2015

For now short answer is to use:

javers.initial(dog);

but i think there is not exacly what you expect, im going to think about it tomorrow :)

@bartoszwalacik

This comment has been minimized.

Member

bartoszwalacik commented Mar 19, 2015

Gents, there is the flag, which does exactly that job
JaversBuilder withNewObjectsSnapshot(true);
shoud be described in the /documentation ..

@AzraelCole could you confirm if it solves your issue?

@AzraelCole

This comment has been minimized.

AzraelCole commented Mar 19, 2015

Thanks for your replies.
Unfortunately my problem was not solved:

In my example the field "id" is not an database id (bad example, sorry), only a number that shall be ignored (I do not have entities, but simple domain objects).
Anyway I played around with that annotation combined with the

withNewObjectsSnapshot(true) 

Adjustments in Pet class:

  @Id
  private int id;

now the code

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

    Diff diff = javers.compare(person1, person2);

    List<ValueChange> changesByType = diff.getChangesByType(ValueChange.class);
    for (ValueChange valueChange : changesByType) {
      System.out.println(valueChange);
    }

    List<NewObject> newObjects = diff.getChangesByType(NewObject.class);
    for (NewObject newObject : newObjects) {
      Object cdo = newObject.getAffectedCdo();
      System.out.println("new object: " + javers.initial(cdo));
    }

    List<ObjectRemoved> removedObjects = diff.getChangesByType(ObjectRemoved.class);
    for (ObjectRemoved objectRemoved : removedObjects) {
      Object cdo = objectRemoved.getAffectedCdo();
      System.out.println("removed object: " + javers.initial(cdo));
    }

produces the output:

ValueChange{globalId:'Pet/2', property:'name', oldVal:'', newVal:'dog'}
ValueChange{globalId:'Pet/2', property:'age', oldVal:'0', newVal:'2'}
ValueChange{globalId:'Pet/2', property:'id', oldVal:'0', newVal:'2'}
ValueChange{globalId:'Pet/3', property:'name', oldVal:'', newVal:'mouse'}
ValueChange{globalId:'Pet/3', property:'age', oldVal:'0', newVal:'3'}
ValueChange{globalId:'Pet/3', property:'id', oldVal:'0', newVal:'3'}
ValueChange{globalId:'Person/', property:'name', oldVal:'Peter', newVal:'Peter Parker'}
new object: Diff:
1. NewObject{globalId:'Pet/3'}
2. ValueChange{globalId:'Pet/3', property:'name', oldVal:'', newVal:'mouse'}
3. ValueChange{globalId:'Pet/3', property:'age', oldVal:'0', newVal:'3'}
4. ValueChange{globalId:'Pet/3', property:'id', oldVal:'0', newVal:'3'}

new object: Diff:
1. NewObject{globalId:'Pet/2'}
2. ValueChange{globalId:'Pet/2', property:'name', oldVal:'', newVal:'dog'}
3. ValueChange{globalId:'Pet/2', property:'age', oldVal:'0', newVal:'2'}
4. ValueChange{globalId:'Pet/2', property:'id', oldVal:'0', newVal:'2'}

removed object: Diff:
1. NewObject{globalId:'Pet/1'}
2. ValueChange{globalId:'Pet/1', property:'name', oldVal:'', newVal:'cat'}
3. ValueChange{globalId:'Pet/1', property:'age', oldVal:'0', newVal:'1'}
4. ValueChange{globalId:'Pet/1', property:'id', oldVal:'0', newVal:'1'}

The last block "removed object" seems not to be correctly:
Why is there a NewObject? I would expect an ObjectRemoved.

@bartoszwalacik

This comment has been minimized.

Member

bartoszwalacik commented Mar 19, 2015

Could you please distill this issue as a small project with a failing test and push it to
the github?

@bartoszwalacik

This comment has been minimized.

Member

bartoszwalacik commented Mar 29, 2015

Hi, sorry, I gave you wrong answer.

JaversBuilder.withNewObjectsSnapshot(true) doesn't solve the problem

You are right that when you are querying for changes, you don't get the initial change connected to NewObject. In fact it's not a real change but let's say: value creation.
It could be represend as a change from null to initial value ...

@bartoszwalacik

This comment has been minimized.

Member

bartoszwalacik commented Apr 2, 2015

think I will add this feature (lets call it 'initial value at change history') in v 1.2

@bartoszwalacik bartoszwalacik added enhancement and removed question labels Apr 2, 2015

@bartoszwalacik bartoszwalacik added JQL and removed enhancement labels Apr 12, 2015

@bartoszwalacik

This comment has been minimized.

Member

bartoszwalacik commented Apr 12, 2015

will be solved in 1.2

@bartoszwalacik

This comment has been minimized.

Member

bartoszwalacik commented Apr 29, 2015

JQL is released in javers 1.2.0
take a look at new query API and examples
http://javers.org/documentation/jql-examples/
http://javers.org/documentation/jql-examples/#new-object-filter

Give us a star if you like JaVers

@bartoszwalacik

This comment has been minimized.

Member

bartoszwalacik commented Apr 30, 2015

if you are using SQL DB, there is a schema migration script

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment