无法从实体输出数据

huangapple go评论75阅读模式
英文:

Cannot output data from the entity

问题

无法将房屋实体的公寓整体列表和按ID单独显示。当我访问该页面时,出现错误,指示无限循环:

(无法编写JSON:无限递归(stackoverflowerror);嵌套异常-com.fasterxml.Jackson.databind.JsonMappingException:无限递归(StackOverflowError)(通过链接链:task.home pageproject.model.Home [“city”] - > task.home pageproject.model.City $ HibernateProxy $ ikPQOTJQ [“home”]))。

HouseRestController

@GetMapping("/{id}")
@PreAuthorize("hasAuthority('user:read')")
public House userPostInfo(@PathVariable(value = "id") Long id) {
    Optional<House> house = houseRepository.findById(id);
    List<House> res = new ArrayList<>();
    house.ifPresent(res::add);

    return res.stream().filter(houses -> houses.getId().equals(id))
            .findFirst()
            .orElse(null);
}
英文:

It is not possible to display apartments from the house entity as a whole list and separately by ID. When I go to the page, I get an error that an infinite loop

> '(failed to write JSON: infinite recursion (stackoverflowerror); nested exception-com.fasterxml.Jackson.databind.JsonMappingException: infinite recursion (StackOverflowError) (via link chain: task.home pageproject.model.Home["city"]->task.home pageproject.model.City$HibernateProxy$ikPQOTJQ["home"])') '.

HouseRestController


@GetMapping(&quot;/{id}&quot;)
    @PreAuthorize(&quot;hasAuthority(&#39;user:read&#39;)&quot;)
    public House userPostInfo(@PathVariable(value = &quot;id&quot;) Long id) {
        Optional&lt;House&gt; house = houseRepository.findById(id);
        List&lt;House&gt; res = new ArrayList&lt;&gt;();
        house.ifPresent(res::add);

        return res.stream().filter(houses -&gt; houses.getId().equals(id))
                .findFirst()
                .orElse(null);
    }

答案1

得分: 2

这个错误是因为你的实体之间的映射方式。请注意你在以下实体之间建立了双向关系:

House - City

House - Contract

User - Contract

由于你在这些实体中使用了 Lombok 的 @Data 注解,会导致以下情况发生:

> @Data 生成了通常与简单 POJO(普通的 Java 对象)和 Bean 关联的所有样板代码:为所有字段生成 getter 方法,为所有非 final 字段生成 setter 方法,并生成适当的 toString、equals 和 hashCode 实现,这些实现涉及类的字段。

因此,由于你没有从任何一侧排除任何数据,每当需要为实体计算 hashCode 时,例如,将其存储在使用哈希表的集合中时,它将失败,因为它会陷入无限循环。当 Jackson 尝试序列化你的实体时也会出现相同的情况。由于你具有双向映射并且没有排除任何一侧,它将尝试递归执行,直到抛出 StackOverflowError。

我建议不要使用 @Data,而是尝试只使用你真正需要的注解,比如 @Getter。然而,为了避免这个无限循环,你可以排除关系的一侧,不包括在 equals 和 hashCode 方法中,也不包括在 Jackson 的序列化/反序列化中:

Contract.java

package task.homerent.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

import javax.persistence.*;
import java.util.Date;

@Data
@Entity
@Table(name = "contract", schema = "public")
public class Contract {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    // ManyToMany to House
    @ManyToOne
    @JoinColumn(name = "id_house")
    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @JsonIgnore
    private House house;

    // ManyToMany to User
    @ManyToOne
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    @JsonIgnore
    @JoinColumn(name = "id_tenant")
    private User user;

    @Column(name = "end_date", nullable = false)
    private Date endDate;
    @Column(name = "start_date", nullable = false)
    private Date id_house;
}

User.java

package task.homerent.model;

import lombok.Data;

import javax.persistence.*;
import java.util.List;
import java.util.Set;

@Data
@Entity
@Table(name = "users", schema = "public")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "user")
    private Set<Contract> contract;
    @Column(name = "email")
    private String email;
    @Column(name = "password")
    private String password;
    @Column(name = "first_name")
    private String firstName;
    @Column(name = "last_name")
    private String lastName;
    @Enumerated(value = EnumType.STRING)
    @Column(name = "role")
    private Role role;
    @Enumerated(value = EnumType.STRING)
    @Column(name = "status")
    private Status status;
}

City.java

package task.homerent.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

import javax.persistence.*;
import java.util.Set;

@Data
@Entity
@Table(name = "city", schema = "public")
public class City {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @OneToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
            CascadeType.REFRESH
    }, mappedBy = "city")
    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @JsonIgnore
    private Set<House> house;

    @Column(name = "id_region", nullable = false)
    private Integer id_region;
    @Column(name = "name", nullable = false)
    private String name;
}
英文:

This error occurs because of the way your entities are mapped. See that you have bidirectional relationships between:

House - City

House - Contract

User - Contract

Because you are using in those entities Lombok's annotation @Data this is what happens:

> @Data generates all the boilerplate that is normally associated with
> simple POJOs (Plain Old Java Objects) and beans: getters for all
> fields, setters for all non-final fields, and appropriate toString,
> equals and hashCode implementations that involve the fields of the
> class

So because you are not excluding any data from any of the sides, whenever there's a need to calculate hashCode for your entity, to for example, store it inside any collection that uses hashTables, it will fail, because it will fall into an infinite loop. Same thing happens when Jackson is trying to serialize your entities. Because you have bidirectional mappings and you are not excluding any side of it, it will try recursively until StackOverflowError is thrown.

I'd advise not to use @Data and instead try to use annotations which you really need, like @Getter. However what you could do, to avoid this infinite loop is to exclude one side of the relationship from the equals and hashCode methods and also from the Jackson serialization/deserialization:

Contract.java

package task.homerent.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

import javax.persistence.*;
import java.util.Date;

@Data
@Entity
@Table(name = &quot;contract&quot;, schema = &quot;public&quot;)
public class Contract {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = &quot;id&quot;)
    private Long id;

    // ManyToMany к Дому
    @ManyToOne
    @JoinColumn(name = &quot;id_house&quot;)
    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @JsonIgnore
    private House house;

    // ManyToMany к Пользователю
    @ManyToOne
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    @JsonIgnore
    @JoinColumn(name = &quot;id_tenant&quot;)
    private User user;


    @Column(name = &quot;end_date&quot;, nullable = false)
    private Date endDate;
    @Column(name = &quot;start_date&quot;, nullable = false)
    private Date id_house;
}

User.java

package task.homerent.model;

import lombok.Data;

import javax.persistence.*;
import java.util.List;
import java.util.Set;

@Data
@Entity
@Table(name = &quot;users&quot;, schema = &quot;public&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = &quot;user&quot;)
    private Set&lt;Contract&gt; contract;
    @Column(name = &quot;email&quot;)
    private String email;
    @Column(name = &quot;password&quot;)
    private String password;
    @Column(name = &quot;first_name&quot;)
    private String firstName;
    @Column(name = &quot;last_name&quot;)
    private String lastName;
    @Enumerated(value = EnumType.STRING)
    @Column(name = &quot;role&quot;)
    private Role role;
    @Enumerated(value = EnumType.STRING)
    @Column(name = &quot;status&quot;)
    private Status status;
}

City.java

package task.homerent.model;


import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

import javax.persistence.*;
import java.util.Set;

@Data
@Entity
@Table(name = &quot;city&quot;, schema = &quot;public&quot;)
public class City {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = &quot;id&quot;)
    private Long id;

    @OneToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
            CascadeType.REFRESH
    }, mappedBy = &quot;city&quot;)
    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @JsonIgnore
    private Set&lt;House&gt; house;

    @Column(name = &quot;id_region&quot;, nullable = false)
    private Integer id_region;
    @Column(name = &quot;name&quot;, nullable = false)
    private String name;
}

huangapple
  • 本文由 发表于 2020年9月26日 17:09:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/64075813.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定