我正在使用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)
...


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

最佳答案

简短答案

@EqualsAndHashCode.Exclude批注添加到property的字段PropertySale

长答案

发生这种情况是因为:


default implementation of Set used by Hibernate is HashSet,基于其元素的哈希码存储它们,并...
由于您正在使用Lombok的@Data批注,因此哈希码(以及equals和toString)实现将所有类字段都考虑在内。这意味着Property.hashCode()调用PropertySale.hashCode(),反之亦然,从而导致每次调用它们中的任何一个时都会出现堆栈溢出错误(如果您使用以下任何一个调用.equals().toString(),也会发生堆栈溢出错误这两个类)。


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


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


奖金

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

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

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

10-14 11:54