Home > Net >  Why bi-directional ManyToOne causes circular dependency in Hibernate?
Why bi-directional ManyToOne causes circular dependency in Hibernate?

Time:11-03

I have entities in my project(based on 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;
}

and

@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;
}

I want to create bi-directional one-to-many relation, when one user can have several rents and when we can select rents by concrete user_id, but something goes wrong

In response I get this:

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

And this is unlimited and logically I have out of memory error. How can I fix this? what I'm doing wrong?

CodePudding user response:

You have two solutions :

  1. Use @JsonIgnore on the @ManyToOne
  2. Do NOT serialize your entities. Use DTOs instead and take care while mapping to avoid circular dependencies

CodePudding user response:

You can also use @JsonManagedReference and @JsonBackReference to solve this infinite recursion problem as follows:

@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;
}

Nevertheless, I would first map your entities to DTOs. However, you might as well need to use @JsonManagedReference and @JsonBackReference in these new DTO classes if you want all data to be available (one way to avoid this infinite recursion problem would be not mapping userAccount in RentInfoDto but you might not want that because you also want to serialize UserAccountDto data).

  • Related