【问题标题】:javers comparing lists in object giving inconsistent resultsjavers 比较对象中的列表给出不一致的结果
【发布时间】:2018-10-07 10:06:00
【问题描述】:

我很难理解为什么下面的结果不一样。

如果我使用 Javers 仅比较两个列表(其中项目的顺序不同),那么我没有发现任何差异,因为我指定了 AS_SET 列表比较来忽略列表中项目的顺序。

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

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'

【问题讨论】:

    标签: javers


    【解决方案1】:

    免责声明:实际上我以前从未听说过 JaVers,我只是偶然发现了这个问题并开始好奇。所以我在这里完全是个新手。

    查看 JaVers 手册中的 diff examples,您会看到如下内容:

    配置

    JaVers 需要知道Employee 类是一个Entity (...) 用@Id 注解来注解name 字段就足够了(...)

    让我们试试这个:

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

    控制台日志变成:

    Diff:
    
    Diff:
    

    【讨论】:

      【解决方案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;
      }
      

      目前截至 18 年 5 月 2 日,由于稍后解释的错误,这不起作用

      方法 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? /限制?

      如果您在没有.registerEntity(new EntityDefinition(ListItem.class, "itemName")) 的情况下查看我们的Method2 的输出,现在代码中发生的另一件事如下所示

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

      首先,它仅使用set 比较list,并添加如下更改

      * 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,而是首先对listset 以及在单个项目级别进行差异。我提出了一个相同的问题以便更好地理解它

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

      【讨论】:

        【解决方案3】:

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

        看起来,目前您无法使用 AS_SET 算法比较 List 中的值对象。在这种情况下,生成 ValueObjectId 存在问题。这里继续讨论https://github.com/javers/javers/issues/669

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多