Merv
Merv

Reputation: 1149

Spring Data JPA @OneToMany infinite loop exception

OneToMany relationship causing infinite loop using Spring Data JPA with hibernate as provider

The problem here is not the type of exception but the infinite loop that causes this exception

Infinite loop

I tried @JsonIgnoreProperties which gives me another error => 'Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer'

The post referencing the solution does not have a solution that adresses my problem.

One says use @JsonManagedReference and @JsonBackReference that does stop the recursion but excludes the object (UserGroup in 'myUser' entity) from the result which I need when I want an object of 'myUser' entity.

The other one says about overriding ToString method which I don't do.

Another one explains why there is an infinite loop and suggest as solution to not do that way. I quote "Try to create DTO or Value Object (simple POJO) without cycles from returned model and then return it."

And this one Difference between @JsonIgnore and @JsonBackReference, @JsonManagedReference explains the difference but doing so I will have the same problem as the first one

'myUser' entity

@Entity
public class MyUser {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String firstName;
  private String lastName;
  private String email;
  private Integer age;

  //@JsonIgnoreProperties({"myUsers"})
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "userGroupId")
  private UserGroup userGroup;

'UserGroup' entity

@Entity
public class UserGroup {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Integer groupOrder;
    @OneToMany
    (
        mappedBy = "userGroup", 
        cascade = CascadeType.ALL, 
        orphanRemoval = true
    )
    private List<MyUser> myUsers;

Upvotes: 7

Views: 6624

Answers (3)

Donato Amasa
Donato Amasa

Reputation: 856

I think I'm getting the point of your problem. You want to fetch MyUser including the userGroup data without the circular reference.

Based from the solutions you enumerated, I suggest you should still use the @JsonBackReference and @JsonManagedReference to prevent recursion on your entities and for the solution on your problem, you can try to use a mapper (MapStruck) and map the userGroup details to a DTO during the retrieval of data from the service.

DTOs:

public class MyUserDto {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    private Integer age;
    private UserGroupDto userGroupDto;
}

public class UserGroupDto {
    private Long id;
    private Integer groupOrder;
}

Mapper (MapStruck):

@Mapper(componentModel = "spring")
public interface MyUserMapper {

    MyUserMapper INSTANCE = Mappers.getMapper(MyUserMapper.class);
    
    UserGroupDto userGroupToDto(UserGroup userGroup);

    @Mapping(source = "myUser.userGroup", target = "userGroupDto")
    MyUserDto myUserToDto(MyUser myUser);
}

After retrieving the data from your repository, you may then call the myUserToDto method to map the entity to a DTO.

This is just one way of solving your problem.

Upvotes: 1

Yasitha Bandara
Yasitha Bandara

Reputation: 2385

change the getUserGroup() method in your MyUser class as follows.

@Entity
public class MyUser 
{    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String firstName;
    private String lastName;
    private String email;
    private Integer age;

    //@JsonIgnoreProperties({"myUsers"})
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userGroupId")
    private UserGroup userGroup;

    public UserGroup getUserGroup() 
    {
        userGroup.setMyUsers(null);
        return userGroup;
    }
}

Upvotes: 4

cagridursun
cagridursun

Reputation: 124

you need to add @JsonIgnore annotation at @OneToMany

like this

@JsonIgnore   
@OneToMany
    (
        mappedBy = "userGroup", 
        cascade = CascadeType.ALL, 
        orphanRemoval = true
    )
    private List<MyUser> myUsers;

Upvotes: 3

Related Questions