【问题标题】:Why bi-directional ManyToOne causes circular dependency in Hibernate?为什么双向ManyToOne会导致Hibernate中的循环依赖?
【发布时间】:2021-11-03 13:25:41
【问题描述】:

我的项目中有实体(基于Spring-Boot + Hibernate):

@Entity
@Table(name = "user_account")
public class UserAccount {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @NotNull
    @Column(name = "username")
    private String userName;

    @NotNull
    @Column(name = "first_name")
    private String firstName;

    @NotNull
    @Column(name = "last_name")
    private String lastName;

    @NotNull
    @Column(name = "password")
    private String password;

    @CreationTimestamp
    @Column(name = "birthday")
    private LocalDateTime birthday;

    @NotNull
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "role", referencedColumnName = "id")
    private UserRole role;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount", cascade= CascadeType.ALL)
    private Set<RentInfo> rents;
}

和

@Entity
@Table(name = "rent_info")
public class RentInfo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @NotNull
    @ManyToOne(cascade= CascadeType.ALL)
    @JoinColumn(name="user_id")
    private UserAccount userAccount;

    @CreationTimestamp
    @Column(name = "start_date")
    private LocalDateTime startDate;

    @CreationTimestamp
    @Column(name = "end_date")
    private LocalDateTime endDate;

    @Column(name = "status")
    private int status;
}

我想创建双向的一对多关系,当一个用户可以有多个租金并且我们可以通过具体的 user_id 选择租金时,但出现问题

作为回应,我得到了这个:

{
        "id": 1,
        "userName": "test@mail.loc",
        "firstName": "fName",
        "lastName": "lName",
        "password": "test",
        "birthday": "2001-11-03T14:28:14",
        "role": {
            "name": "CLIENT"
        },
        "rents": [
            {
                "userAccount": {
                    "id": 1,
                    "userName": "test@mail.loc",
                    "firstName": "fName",
                    "lastName": "lName",
                    "password": "test",
                    "birthday": "2001-11-03T14:28:14",
                    "role": {
                        "name": "CLIENT"
                    },
                    "rents": [
                        {
                            "userAccount": {
                                "id": 1,
                                "userName": "test@mail.loc",
                                "firstName": "fName",
                                "lastName": "lName",
                                "password": "test",
                                "birthday": "2001-11-03T14:28:14",
                                "role": {
                                    "name": "CLIENT"
                                }
    .....

这是无限的,逻辑上我有内存不足错误。我怎样才能解决这个问题?我做错了什么?

【问题讨论】:

  • @OneToMany 关系默认是惰性的。仅将fetch = FetchType.LAZY 用于@ManyToOne

标签: java spring spring-boot hibernate


【解决方案1】:

你有两个解决方案:

  1. 在@ManyToOne 上使用@JsonIgnore
  2. 不要序列化您的实体。改用 DTO 并在映射时小心避免循环依赖

【讨论】:

  • 改写选项 #2:您可以将实体映射到单独的类(通常称为数据传输对象或 DTO),而不是将 JPA 注释的实体类序列化为 JSON。在映射过程中,您必须自己解决周期问题(例如,不映射rent.userAccount
  • 非常感谢。这解决了我的问题
【解决方案2】:

你也可以使用@JsonManagedReference和@JsonBackReference来解决这个无限递归问题,如下:

@Entity
@Table(name = "user_account")
public class UserAccount {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @NotNull
    @Column(name = "username")
    private String userName;

    @NotNull
    @Column(name = "first_name")
    private String firstName;

    @NotNull
    @Column(name = "last_name")
    private String lastName;

    @NotNull
    @Column(name = "password")
    private String password;

    @CreationTimestamp
    @Column(name = "birthday")
    private LocalDateTime birthday;

    @NotNull
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "role", referencedColumnName = "id")
    private UserRole role;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount", cascade= CascadeType.ALL)
    @JsonManagedReference
    private Set<RentInfo> rents;
}
@Entity
@Table(name = "rent_info")
public class RentInfo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @NotNull
    @ManyToOne(cascade= CascadeType.ALL)
    @JoinColumn(name="user_id")
    @JsonBackReference
    private UserAccount userAccount;

    @CreationTimestamp
    @Column(name = "start_date")
    private LocalDateTime startDate;

    @CreationTimestamp
    @Column(name = "end_date")
    private LocalDateTime endDate;

    @Column(name = "status")
    private int status;
}

不过,我会首先将您的实体映射到 DTO。但是,如果您希望所有数据都可用,您可能还需要在这些新的 DTO 类中使用@JsonManagedReference 和@JsonBackReference(避免这种无限递归问题的一种方法是不在RentInfoDto 中映射userAccount,而是您可能不希望这样,因为您还想序列化 UserAccountDto 数据)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    相关资源
    最近更新 更多