javers比较对象中的列表给出不一致的结果

时间:2018-04-26 16:04:45

标签: javers

我很难理解为什么以下内容不会给我相同的结果。

如果我使用Javers来比较两个列表(它们具有不同顺序的项目),那么我没有区别,因为我已经指定了AS_SET列表比较以忽略列表中项目的顺序。

如果我将这些列表作为对象的属性包装,则Javers返回List的元素不同,因为或列表中的项目顺序。

AS_SET是否应用于对象中的列表?就好像它被忽略了

public class App {
    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("value2")
                        .build(),
                ListItem.builder()
                        .itemName("item1")
                        .itemValue("value")
                        .build()
        );

        TopLevelClass tlc1 = TopLevelClass.builder().items(list1).build();
        TopLevelClass tlc2 = TopLevelClass.builder().items(list2).build();

        Diff diff = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.AS_SET).build().compare(list1, list2);
        System.out.println(diff);

        Diff diffTlc = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.AS_SET).build().compare(tlc1, tlc2);
        System.out.println(diffTlc);
    }
}

以下课程:

package wibble;

import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

@Builder
@Getter
@Setter
@EqualsAndHashCode
public class ListItem {
    private String itemName;
    private String itemValue;
}
package wibble;

import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

import java.util.List;

@Builder
@Getter
@Setter
@EqualsAndHashCode
public class TopLevelClass {
    List<ListItem> items;
}

上述运行时的输出:

Diff:

Diff:
    * changes on wibble.TopLevelClass/ :
    - 'items/0.itemName' changed from 'item1' to 'item2'
    - 'items/0.itemValue' changed from 'value' to 'value2'
    - 'items/1.itemName' changed from 'item2' to 'item1'
    - 'items/1.itemValue' changed from 'value2' to 'value'

3 个答案:

答案 0 :(得分:2)

有不同的方法(很多方法)

方法1 - 使用@Id

更新代码

这类似于@kriegaex建议的内容,是为代码添加@Id注释并更新类,如下所示

public class ListItem {
  @Id
  private String itemName;
  private String itemValue;
}

方法2 - 实体注册

Method 1有一个缺点,它需要对您的实际模型进行代码更改,并且可能并不总是可行或不可取。在这种情况下,您需要手动注册您的实体

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);
}

以上运行的输出低于

* 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

没有.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
  - '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'

方法3 - 使用@IgnoreDeclaredProperties

因此,在稍后的部分澄清之后,另一种方法是在

之下
@IgnoreDeclaredProperties
public class ListItem {
    private String itemName;
    private String itemValue;
}

这将使List比较工作和休息项目未添加。但这不会让您直接比较ListItem

因此,建议的方法是仅使用Method 2,如果您不希望代码更改模型并且还具有完全的灵活性

方法4 - 使用@ShallowReference

可以在项目中添加@ShallowReference,然后进行正确的设置比较

public class TopLevelClass {

    @ShallowReference
    List<ListItem> items;
}

目前截至 02-May-18 ,由于稍后解释的错误,此功能无法正常工作

方法5 - 使用集

如果您需要

,可以在班级中使用Set代替List
public class TopLevelClass {
   Set<ListItem> items;
}

更新的比较代码将是

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();

输出如下

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

方法6 - 将ListItem注册为值

在此,您可以将ListItem注册为值

Javers jvc = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.AS_SET)
            .registerValue(ListItem.class)
            .build();
Diff diffTlc = jvc.compare(tlc1, tlc2);
System.out.println(diffTlc);

输出

* changes on com.javerstest.TopLevelClass/ :
  - 'items' collection changes :
    . 'com.javerstest.ListItem@306a04bf' removed
    . 'com.javerstest.ListItem@306a04fb' added
    . 'com.javerstest.ListItem@29f62baf' added

功能?/ BUG? /限制?

如果您查看我们的Method2没有.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

这基本上是因为ListCompareAlgorithm.AS_SET,如果您将其更改为SIMPLE,输出将更改为

  - 'items' collection changes :
    0. '...ListItem/item1' changed to '...ListItem/item2'
    1. '...ListItem/item2' changed to '...ListItem/item1'
    2. '...ListItem/item3' added

所以在我们的原始代码中

Javers jvc = JaversBuilder.javers().withListCompareAlgorithm(ListCompareAlgorithm.AS_SET).build();
Diff diffTlc = jvc.compare(tlc1, tlc2);

首先,它只使用list set进行比较,并添加如下所示的更改

* 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'

所以不是AS_SET没有被挑选,而是差异首先是list set和个别项目级别。我提出了同样的问题,以便更好地理解

https://github.com/javers/javers/issues/669

答案 1 :(得分:1)

正如@kriegaex已经提到的,您可以将ListItem映射为实体或值,它可以解决问题。但这将是解决方法,因为ListItem看起来像值对象。另一种解决方法是将List更改为Set。

看起来,现在你无法比较列表中的值对象和AS_SET算法。在这种情况下,生成ValueObjectIds存在问题。这里继续讨论https://github.com/javers/javers/issues/669

答案 2 :(得分:0)

免责声明:其实我以前从未听说过JaVers,我只是偶然发现了这个问题而变得很好奇。所以我在这里是一个完整的新手。

查看JaVers手册中的diff examples,您可以阅读以下内容:

  

<强>配置

     

JaVers需要知道$ wget https://storage.googleapis.com/download.tensorflow.org/models/inceptions5h.zip类是Employee(...)这足以使用Entity注释(...)注释名称字段

所以让我们试试这个:

@Id

控制台日志变为:

package wibble;

import lombok.*;
import org.javers.core.metamodel.annotation.Id;

@Builder
@Getter
@Setter
@EqualsAndHashCode
@ToString
public class ListItem {
  @Id
  private String itemName;
  private String itemValue;
}

我希望这就是你想要的。