3

Project Entity's Method

@JsonIgnore
@ManyToMany(mappedBy = "projects")
private List<User> users = new ArrayList<>();

public List<User> getUsers(){
    return users;
}
public void setUsers(List<User> users){
    this.users =users;
}

User Entity's Method

 many to many relationships between users and project...

@ManyToMany(fetch = FetchType.EAGER,cascade = {CascadeType.ALL})
@JoinTable(
        name = "user_project",
        joinColumns = {@JoinColumn(name = "userid")},
        inverseJoinColumns = {@JoinColumn(name="projectid")}
)
private List<Project> projects = new ArrayList<>();

public List<Project> getProjects(){
    return this.projects;
}

public void setProjects(List<Project> projects){
    this.projects = projects;
}

I want to create a many to many relationships between a project entity and a user entity.

I want to get all the user associated with a project when my controller calls the project controller's method. Again when I want to fetch a user I want to get all the projects associated with the user. I am building a REST web application.

If I don't use @JsonIgnor annotation then I get a stack overflow error.

But if I use it inside 'Project Entity' then I don't get all the users associated with it.

Any solution?

forhadmethun
  • 535
  • 5
  • 16
  • https://stackoverflow.com/a/49668812 – Cepr0 Jul 15 '18 at 09:04
  • First (preferred) solution: don't use your persistence model as your API model. Design specific objects (DTOs) containing exactly what you want to return as a response to a REST request, and send that back rather than your entities. Transform your entities to these objects in your controller. Second solution: use Jackson views. – JB Nizet Jul 15 '18 at 09:06
  • If I create a DTO and return the object of @JSONIgnore will it return to the client side? – forhadmethun Jul 20 '18 at 17:08
  • Possible duplicate of [Jackson json two way object reference](https://stackoverflow.com/questions/50976307/jackson-json-two-way-object-reference) – Amith Kumar Jul 20 '18 at 18:36

1 Answers1

3

Set FetchType to LAZY.

@ManyToMany(fetch = FetchType.LAZY ,cascade = {CascadeType.ALL})

Now, even if the data exists on the database, it'll be retrieved as null. If you want them to be returned, then just invoke a getter to this property so hibernate will go and select them from your database.

This is useful to prevent infinite recursion while retrieving data from the database.

UPDATE

The recursive loop will happen because when spring serialize the object to JSON, jackson will use getters and setters to retrieve the data. This way, hibernate will retrieve the data from the database, even if FetchType is equals LAZY.

One workaround is to have a DTO class containing exactly what you want to return.

For example:

User.java

public class User {

    private Long id;
    private String name;
    private Date birthDate;
    private List<Post> posts;

    public User() {}

    public User(Long id, String name, Date birthDate) {
        this.id = id;
        this.name = name;
        this.birthDate = birthDate;
        this.posts = new ArrayList < >();
    }

    // getters and setters..

}

Post.java

public class Post {

    private Long id;
    private String title;
    private String content;
    private User owner;

    public Post() {}

    public Post(Long id, String title, String content, User owner) {
        this.id = id;
        this.title = title;
        this.content = content;
        this.owner = owner;
    }

    // getters and setters..

}

UserDTO.java

public class UserDTO {

    private Long id;
    private String name;
    private Date birthDate;

    public UserDTO(User user) {
        this.id = user.getId();
        this.name = user.getName();
        this.birthDate = user.getBirthDate();
    }

    // getters and setters..

}

PostDTO.java

public class PostDTO {

    private Long id;
    private String title;
    private String content;

    public PostDTO(Post post) {
        this.id = post.getId();
        this.title = post.getTitle();
        this.content = post.getContent();
    }

    // getters and setters..

}

UserService.java

@Service
public class UserService {

    @Autowired
    UserRepository userRepository;

    public List<UserDTO> retrieveAll() {
        List <UserDTO> users = userRepository.findAll().stream().map(user -> new UserDTO(user)).collect(Collectors.toList());

        return users;
    }
}

This will not enter in infinite recursion, because the User posts will not be rendered.

Now, if you don't want to return List of UserDTO (List<UserDTO>), you can create a helper class which returns a user based on the UserDTO informations.

Something like that:

Helper.java

public class Helper {

    public static User userFromDTO(UserDTO userDTO) {
        return new User(userDTO.getId(), userDTO.getName(), userDTO.getBirthDate());
    }
}

Now in your service:

@Service
public class UserService {

    @Autowired
    UserRepository userRepository;

    public List<User> retrieveAll() {
        List<User> users = userRepository.findAll();
        users = users.stream().map(user -> userFromDTO(new UserDTO(user))).collect(Collectors.toList());

        return users;
    }

}
Matheus
  • 3,058
  • 7
  • 16
  • 37
  • When returning the Entity object by invoking the getter method it goes to infinity recursion and shows StackOverflow error. – forhadmethun Jul 20 '18 at 17:31
  • @forhadmethun the solution is, if you don't want to use `@JsonIgnore`, then use a DTO containing what you want to return – Matheus Jul 20 '18 at 17:33