java - 保存 OneToMany 关系时总是获得无限递归(已使用 @JsonBackReference 和 @JsonManagedReference)

标签 java

我正在使用 Spring Boot 编写 OneToMany 关系,一个属性可以有多个 propertySale。

这是我的属性类:

@Data
@Getter
@Entity
@Table(name = "Property")
public class Property {
    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(mappedBy="property", cascade = CascadeType.ALL, targetEntity = PropertySale.class)
    @JsonManagedReference
    private Set<PropertySale> propertySales;
...

这是我的 propertySale 类:

@Data
@Getter
@Entity
@Table(name = "PropertySale")
public class PropertySale {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "property_id", referencedColumnName = "id")
    @JsonBackReference
    private Property property;
...

我像这样保存 propertySale:

@Override
    public ResponseEntity<PropertySale> savePropertySale(PropertySale propertySale) {
        Optional<Property> existPropertyOpt = this.propertyRepository.findById(propertySale.getProperty().getId());

        if(existPropertyOpt.isPresent()){
            Example<PropertySale> propertySaleExample =  Example.of(propertySale);
            Optional<PropertySale> existPropSale = this.propertySaleRepository.findOne(propertySaleExample);
            if(existPropSale.isPresent()){
                throw new PropertySaleAlreadyExistException();

            }else{
                Property existProperty = existPropertyOpt.get();
                propertySale.setProperty(existProperty);
                existProperty.addPropertySale(propertySale);
                this.propertyRepository.save(existProperty);
                return new ResponseEntity<>(propertySale, HttpStatus.CREATED);
            }

        }else{
            throw new PropertyNotFoundException(propertySale.getProperty().getId());
        }
    }

我明白

Caused by: java.lang.StackOverflowError
    at java.util.AbstractSet.hashCode(AbstractSet.java:122)
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:459)
    at com.mikason.PropView.dataaccess.estateEntity.Property.hashCode(Property.java:12)
    at com.mikason.PropView.dataaccess.commercialEntity.PropertySale.hashCode(PropertySale.java:10)
    at java.util.AbstractSet.hashCode(AbstractSet.java:126)
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:459)
    at com.mikason.PropView.dataaccess.estateEntity.Property.hashCode(Property.java:12)
    at com.mikason.PropView.dataaccess.commercialEntity.PropertySale.hashCode(PropertySale.java:10)
    at java.util.AbstractSet.hashCode(AbstractSet.java:126)
...

当我尝试保存特性销售时,有人可以告诉我哪里做错了吗?非常感谢。

最佳答案

简短回答

PropertySale的字段property中添加@EqualsAndHashCode.Exclude注释。

长答案

发生这种情况是因为:

  1. default implementation of Set used by Hibernate is HashSet,它基于其元素的哈希码来存储它们,并且...

  2. 由于您使用的是 Lombok 的 @Data 注释,因此哈希码(以及 equals 和 toString)实现会考虑所有类字段。这意味着 Property.hashCode() 调用 PropertySale.hashCode() ,反之亦然,导致每当调用其中任何一个时都会出现堆栈溢出错误(这如果您使用这两个类中的任何一个调用 .equals().toString() ,也会发生这种情况。

为了解决此问题,您可以使用一些选项:

  • Property 类中的 @Data 替换为 @Getter@Setter。由于它不用作 Set 内的元素,因此它可能 doesn't need to override hashCode/equals ,与 PropertySale 不同。
  • 在字段 PropertySale.property 上添加 @EqualsAndHashCode.Exclude(和 @ToString.Exclude),因此 PropertySale.hashCode 不会调用 Property.hashCode
  • PropertySale 编写您自己的 hashCode/equals 实现(在这种情况下,Lombok 不会生成它们),而无需调用 Property .hashCode(例如,您仍然可以使用 Property.id)。

奖金

正如我提到的,toString 可能会出现同样的问题,但修正方法与 equals/hashCode 几乎相同:ToString.Exclude/避免@Data/自定义实现...

您还可以编写单元测试,以确保在运行应用程序时这些方法都不会抛出 StackOverflowError

关于java - 保存 OneToMany 关系时总是获得无限递归(已使用 @JsonBackReference 和 @JsonManagedReference),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57932048/

相关文章:

java - 你如何获得按钮的背景颜色?

java - WebDriverException : unknown error: document. getBoxObjectFor 不是通过 Java 使用 Selenium 与 ChromeDriver 和 Chrome 的函数

java - "Missing return Statement"但我已经有了 return 语句

java - 在recyclerView中获取json数据

java - 如何在 JMeter 中使用 beanshell 后处理器生成随机数

java - 为什么 javax.sql 包中的类使用 new String(str)?

java - HQL 更新和域对象

java - 我必须实现 CompareTo 吗?

java - 如何在没有所有子节点文本的情况下获取直接元素的文本

java - Hazelcast - 从 map 中删除过期的 session