There are different ways of doing it (lots of them)
Method 1 - Update Code with @Id
This similar to what @kriegaex
suggested and is to add a @Id
annotation to your code and update the class like below
public class ListItem {
@Id
private String itemName;
private String itemValue;
}
Method 2 - Entity Registration
The Method 1
has a downside that it needs code changes to your actual model and may not be always possible or desirable. In this case you need to manually register your entity
public static void main(String[] args) {
List<ListItem> list1 = ImmutableList.of(
ListItem.builder()
.itemName("item1")
.itemValue("value")
.build(),
ListItem.builder()
.itemName("item2")
.itemValue("value2")
.build()
);
List<ListItem> list2 = ImmutableList.of(
ListItem.builder()
.itemName("item2")
.itemValue("value2change")
.build(),
ListItem.builder()
.itemName("item1")
.itemValue("value")
.build(),
ListItem.builder()
.itemName("item3")
.itemValue("value3")
.build()
);
TopLevelClass tlc1 = TopLevelClass.builder().items(list1).build();
TopLevelClass tlc2 = TopLevelClass.builder().items(list2).build();
Javers jvc = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.AS_SET)
.registerEntity(new EntityDefinition(ListItem.class, "itemName"))
.build();
Diff diffTlc = jvc.compare(tlc1, tlc2);
System.out.println(diffTlc);
}
The output of above run is below
* changes on com.javerstest.ListItem/item2 :
- 'itemValue' changed from 'value2' to 'value2change'
* changes on com.javerstest.TopLevelClass/ :
- 'items' collection changes :
. 'com.javerstest.ListItem@306a04bf' removed
. 'com.javerstest.ListItem@306a04fb' added
. 'com.javerstest.ListItem@29f62baf' added
* new object: com.javerstest.ListItem/item3
And without the .registerEntity(new EntityDefinition(ListItem.class, "itemName"))
, it is
* changes on com.javerstest.TopLevelClass/ :
- 'items' collection changes :
. 'com.javerstest.ListItem@306a04bf' removed
. 'com.javerstest.ListItem@306a04fb' added
. 'com.javerstest.ListItem@29f62baf' added
- 'items/0.itemName' changed from 'item1' to 'item2'
- 'items/0.itemValue' changed from 'value' to 'value2change'
- 'items/1.itemName' changed from 'item2' to 'item1'
- 'items/1.itemValue' changed from 'value2' to 'value'
Method 3 - Using @IgnoreDeclaredProperties
So after clarification on later section, another way to do it is below
@IgnoreDeclaredProperties
public class ListItem {
private String itemName;
private String itemValue;
}
This will let the List
compare work and rest items not added. But this will not let you compare a ListItem
directly.
So the recommended way is to use Method 2
only, if you don't want code changes to your models and also complete flexibility
Method 4 - Using @ShallowReference
One can add a @ShallowReference
to the items and it will then do a proper Set comparison
public class TopLevelClass {
@ShallowReference
List<ListItem> items;
}
Currently as of 02-May-18 this doesn't work because of the bug explained later
Method 5 - Using Sets
You can use Set
instead of List
in your class if you want
public class TopLevelClass {
Set<ListItem> items;
}
And the updated comparison code will be
TopLevelClass tlc1 = TopLevelClass.builder().items(new HashSet<ListItem>(list1)).build();
TopLevelClass tlc2 = TopLevelClass.builder().items(new HashSet<ListItem>(list2)).build();
Javers jvc = JaversBuilder.javers().build();
With the output as below
Diff:
* new object: com.javerstest.TopLevelClass/#items/bd3fdf9ee4c8eb797ca392a1f8eb28c6
* new object: com.javerstest.TopLevelClass/#items/ad5b96d68b6742a92d330f0d98bae8b3
* object removed: com.javerstest.TopLevelClass/#items/a1961e7fd2e23b166e4d1b2acbe67263
* changes on com.javerstest.TopLevelClass/ :
- 'items' collection changes :
. 'com.javerstest.TopLevelClass/#items/a1961e7fd2e23b166e4d1b2acbe67263' removed
. 'com.javerstest.TopLevelClass/#items/bd3fdf9ee4c8eb797ca392a1f8eb28c6' added
. 'com.javerstest.TopLevelClass/#items/ad5b96d68b6742a92d330f0d98bae8b3' added
Method 6 - Register ListItem as value
In this you can register ListItem
as a value
Javers jvc = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.AS_SET)
.registerValue(ListItem.class)
.build();
Diff diffTlc = jvc.compare(tlc1, tlc2);
System.out.println(diffTlc);
And the output is
* changes on com.javerstest.TopLevelClass/ :
- 'items' collection changes :
. 'com.javerstest.ListItem@306a04bf' removed
. 'com.javerstest.ListItem@306a04fb' added
. 'com.javerstest.ListItem@29f62baf' added
Feature?/ BUG? /limitation?
Now another thing that is happening here in the code is below, if you look at the output from our Method2
without .registerEntity(new EntityDefinition(ListItem.class, "itemName"))
* changes on com.javerstest.TopLevelClass/ :
- 'items' collection changes :
. 'com.javerstest.ListItem@306a04bf' removed
. 'com.javerstest.ListItem@306a04fb' added
. 'com.javerstest.ListItem@29f62baf' added
This is basically because of ListCompareAlgorithm.AS_SET
, if you change it to SIMPLE
, the output will change to below
- 'items' collection changes :
0. '...ListItem/item1' changed to '...ListItem/item2'
1. '...ListItem/item2' changed to '...ListItem/item1'
2. '...ListItem/item3' added
So in our original code
Javers jvc = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.AS_SET).build();
Diff diffTlc = jvc.compare(tlc1, tlc2);
First it compares the list
using set
only and adds the change like below
* changes on com.javerstest.TopLevelClass/ :
- 'items' collection changes :
. 'com.javerstest.ListItem@306a04bf' removed
. 'com.javerstest.ListItem@306a04fb' added
. 'com.javerstest.ListItem@29f62baf' added
But then it goes further again and does another diff on each item of the array as well, that is why an additional diff gets added
- 'items/0.itemName' changed from 'item1' to 'item2'
- 'items/0.itemValue' changed from 'value' to 'value2change'
- 'items/1.itemName' changed from 'item2' to 'item1'
- 'items/1.itemValue' changed from 'value2' to 'value'
So it is not that the AS_SET
is not being picked, it is that the diff is first done for the list
as set
and at individual item level also. I have raise a issue about the same to understand it better
https://github.com/javers/javers/issues/669