保存OneToMany关系时始终获得无限递归(已使用@JsonBackReference和@JsonManagedReference)

时间:2019-09-14 02:37:54

标签: 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;
...

我这样保存财产出售:

@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)
...

当我尝试保存房地产时,有人可以告诉我我做错了什么吗?非常感谢。

1 个答案:

答案 0 :(得分:1)

简短答案

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

长答案

发生这种情况是因为:

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

  2. 由于使用的是Lombok的@Data批注,因此哈希码(以及equals和toString)实现将所有类字段都考虑在内。这意味着Property.hashCode()会调用PropertySale.hashCode(),反之亦然,这会导致您每次调用它们中的任何一个时都会遇到堆栈溢出错误(如果您调用.equals().toString()和这两个类别中的任何一个。

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

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

奖金

正如我提到的,Property.id可能会出现相同的问题,但是更正与toString / equals相同:ToString.Exclude /避免使用{{1} } /自定义实现...

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