Home > Software engineering >  How to solve circular reference in JPA associations?
How to solve circular reference in JPA associations?

Time:05-19

I know this kind of question was answered many times and there are solutions to it, however, none of them worked for me. I tried @JsonIgnore, @JsonIgnoreProperties @JsonManagedReference/@JsonBackedReference, yet still the debugger shows that user has reference to authority, which has reference to user, which has reference to authority, which has reference to user ... Most importantly it doesn't throw any exceptions. However, I still wonder why does this happen, why it doesn't throw exceptions, and does it affect productivity

My entities are simple: there is a User

@Entity
@Table(name = "users_tb")
@NoArgsConstructor
@Getter
@Setter
public class User {

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

    private String username;

    private String password;

    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Authority> authorities;
}

and Authority

@Entity
@Table(name = "authorities_tb")
@NoArgsConstructor
@Getter
@Setter
public class Authority {

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

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
}

the code to retrieve users using JpaRepository<T, V>

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        var user = userRepository.findUserByUsername(username).orElseThrow(
              () -> new UsernameNotFoundException(ERR_USERNAME_NOT_FOUND));

        return new CustomUserDetails(user);
    }

The debugger output state before return from loadUserByUsername:

user = {User@10781}
    > id = {Long@10784}
    > username = "John"
    > password = "$2a$10$xn3LI/AjqicFYZFruSwve.681477XaVNaUQbr1gioaWPn4t1KsnmG" 
    > authorities = {PersistentBag@10788} size = 2
        > 0 = {Authority@10818}
            > id = {Long@10784}
            > name = "READ"
            > user = {User@10784}
                > id = {User@10784}
                > username = "John"
                > password = "$2a$10$xn3LI/AjqicFYZFruSwve.681477XaVNaUQbr1gioaWPn4t1KsnmG" 
                > authorities = {PersistentBag@10788} size = 2
                    > 0 = {Authority@10818}
                    ...

CodePudding user response:

Try not to use the Lombok annotation @Getter and @Setter. Then generate manually getters and setters and use @JsonIgnore on the class member field and the getter, and @JsonProperty on the setter.

@JsonIgnore
private List<Authority> authorities;

@JsonIgnore
// Getter for authorities

@JsonProperty
// Setter for authorities

CodePudding user response:

You can simply annotate the duplicated field with @ToString.Exclude

In you case:

@Data // this includes getter/setter and toString
@Entity
@Table(name = "users_tb")
@NoArgsConstructor
public class User {

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

    private String username;

    private String password;

    @ToString.Exclude
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Authority> authorities;
}

@Data
@Entity
@Table(name = "authorities_tb")
@NoArgsConstructor
public class Authority {

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

    private String name;

    @ToString.Exclude
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
}

More info: Lombok @Data and @ToString.Exclude

  • Related