Lombok - java.lang.StackOverflowError: null on toString meth



我有两个类ProductCategorie。当我想使用以下代码categoryRepository.save(c1)修改分类中的产品列表时,会发生此错误:

 java.lang.StackOverflowError: null
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449) ~[na:1.8.0_191]
        at java.lang.StringBuilder.append(StringBuilder.java:136) ~[na:1.8.0_191]
        at org.sid.entities.Product.toString(Product.java:12) ~[classes/:na]
        at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_191]
        at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_191]
        at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_191]
        at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_191]
        at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_191]
        at org.sid.entities.Categorie.toString(Categorie.java:15) ~[classes/:na]
        at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_191]

@Document
@Data @AllArgsConstructor @NoArgsConstructor @ToString
public class Categorie {
    @Id
    private String id;
    private String name;
    @DBRef
    @JsonIgnore
    private Collection<Product> products=new ArrayList<>();
@Document
@Data @AllArgsConstructor @NoArgsConstructor @ToString
public class Product {
    @Id
    private String id;
    private String name;
    private double price;
    @DBRef
    private Categorie categorie;
@Bean
    CommandLineRunner start(CategoryRepository categoryRepository, ProductRepository productRepository){
        return args -> {
            categoryRepository.deleteAll();
            Stream.of("c1 Ordinateur","c2 Imprimente").forEach(c->{
                categoryRepository.save(new Categorie(c.split(" ")[0],c.split(" ")[1],new ArrayList<>()));
            });
            categoryRepository.findAll().forEach(System.out::println);
            productRepository.deleteAll();
            Categorie c1=categoryRepository.findById("c1").get();
            Stream.of("P1","P2","P3","P4").forEach(name->{
               Product p= productRepository.save(new Product(null,name,Math.random()*1000,c1));
               c1.getProducts().add(p);
               categoryRepository.save(c1);
            });
productRepository.findAll().forEach(p->{
                System.out.println(p.toString());
            });
        };
    }

任何人都可以有解决这个问题的想法吗?谢谢。

您在龙目岛生成的toString方法中有一个循环引用。

  • Product引用Categorie on toString,引用Product,依此类推

您可以使用 排除属性 @ToString ,但它很快就会被弃用,因此请使用 @ToString.Exclude

@Document
@Data @AllArgsConstructor @NoArgsConstructor @ToString
public class Product {
  ...
  @ToString.Exclude
  private Categorie categorie;
  ...
}
@Document
@Data @AllArgsConstructor @NoArgsConstructor @ToString
public class Categorie {
  ...
  @ToString.Exclude
  private Collection<Product> products=new ArrayList<>();
  ...
}

龙目岛参考这里和这里

我假设@ToString注释会告诉您正在使用某个工具(龙目岛?(来生成打印所有字段值的toString方法。每个类都引用另一个类:产品有一个分类,分类有一个产品实例列表。因此,当 toString 实现打印一个 Categorie 时,它会在每个 Product 上调用 toString,然后在它的 Categorie 上调用 toString,依此类推。由于 Product 可能是指在其产品列表中包含该产品的分类,因此 toString 调用会来回反弹,直到堆栈溢出。解决方案是避免从toString方法打印Categorie,products或Product.categorie。如果您使用的是龙目岛,请尝试使用 @ToString.Exclude 注释 Categorie.products。

我在Spring JPA实现中遇到了类似的问题,并且使用龙目岛@Data注释了实体,该一起生成@ToString,@EqualsAndHashCode,@Getter/@Setter和@RequiredArgsConstructor。然而@ToString,@EqualsAndHashCode注释生成具有实体所有属性的代码,包括通常引用其他实体和其他一对一关系实体的集合。这将导致循环引用,也会导致加载惰性集合(默认情况下,JPA 以惰性方式加载一对多和多对多(。

添加@ToString.Exclude@EqualsAndHashCode.Exclude会导致代码繁琐,强烈建议避免在 JPA 实体上使用@Data或@ToString@EqualsAndHashCode。最好使用 IDE 生成 toString、equals、hashCode 并在使用前对其进行验证(即非空字段与空字段等(。

最新更新