Skip to content
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
Closed

lists: newObject and ValueChange? #129

AzraelCole opened this issue Mar 18, 2015 · 10 comments

Comments

@AzraelCole
Copy link

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Member

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
Copy link
Author

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
Copy link
Member

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

@bartoszwalacik
Copy link
Member

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
Copy link
Member

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

@bartoszwalacik
Copy link
Member

will be solved in 1.2

@bartoszwalacik
Copy link
Member

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
Copy link
Member

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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants