6

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;
Merv
  • 1,039
  • 2
  • 12
  • 22
  • Possible duplicate of [Infinite loop with spring-boot in a one to many relation](https://stackoverflow.com/questions/30892298/infinite-loop-with-spring-boot-in-a-one-to-many-relation) – Tom Feb 22 '18 at 17:42
  • Do you need help still? – VK321 Apr 12 '18 at 18:50

3 Answers3

4

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;
    }
}
Yasitha Bandara
  • 1,995
  • 2
  • 14
  • 20
2

you need to add @JsonIgnore annotation at @OneToMany

like this

@JsonIgnore   
@OneToMany
    (
        mappedBy = "userGroup", 
        cascade = CascadeType.ALL, 
        orphanRemoval = true
    )
    private List<MyUser> myUsers;
cagridursun
  • 114
  • 4
  • Thanks for your reply. The problem with @JsonIgnore is that it ignores the 'myUsers' field completely in my results when I'm fething 'UserGroup' entities. And I need that field to be in my results when doing the latter. – Merv Feb 23 '18 at 15:06
1

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.

Donato Amasa
  • 846
  • 2
  • 6
  • 22
  • Thanks bro. I did indeed solved it some time ago with DTO. In other words I recommend stop fetching data directly with your entities to save time with these errors and configurations of ignoring attributes etc. Its practically easier to do this with DTO you only fetch data that you need and it gives you more flexibility. I also recommend if your using spring to use lombok annotation package. Thx – Merv Jan 06 '21 at 23:08
  • sure thing bro! – Donato Amasa Jan 06 '21 at 23:32