2

I am using Spring Boot and Jackson and Hibernate to create an API. Hibernate connects to a MySQL database. I understand the good practices but I'm stuck on a particular point. I have an n:m relationship that contains an extra field.

Ex: Author(id, ...) -> Written(idAuthor, idBook, date) <- Book(id, ...)

I understand how to map a traditional n:m relationship, but this technique does not apply to me this time.

For this, I found a source on the internet that showed the solution: create an intermediate class in my code that contains an Author type object and a Book type object + my additional fields.


@Entity
@Table(name = "Author")
public class Author implements Serializable {
/...
 
    @Id
    @GeneratedValue
    private int id;

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
    private Set<Written> written= new HashSet<>();
/...
}

@Entity
@Table(name = "Book")
public class Book implements Serializable{
/...

    @Id
    @GeneratedValue
    private int id;

    @OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
    private Set<Written> written= new HashSet<>();
/...
}


public class Written implements Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = "idAuthor")
    private Author author;

    @Id
    @ManyToOne
    @JoinColumn(name = "idBook")
    private Book book;

    //Extra fields ....

}

That's a bidirectional link. With this code, I get an infinite recursivity error:

Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.exampleAPI.api.model.Book["written"])]

I tried to use @JsonIgnore, @JsonManagedReference and @JsonBackReference on the Written class, also tried to use transient keyword, but nothing worked.

I can't find any source on the internet that could help me, and neither can the documentation for this particular case.

Can someone help me?

nicooooo
  • 33
  • 5
  • Similar questions: [Jackson serialize problem in @ManyToMany relationship](https://stackoverflow.com/questions/61392551/jackson-serialize-problem-in-manytomany-relationship/61398920#61398920), [Jackson/Hibernate, meta get methods and serialization](https://stackoverflow.com/questions/55383889/jackson-hibernate-meta-get-methods-and-serialization/55386877#55386877) – Michał Ziober Oct 09 '20 at 15:04

1 Answers1

1

When unhandled bidirectional relationship occurs, Jackson faces infinite recursion.

I tried to use @JsonIgnore, @JsonManagedReference and @JsonBackReference on the Written class

You need to use @JsonManagedReference and @JsonBackReference annotations separately to prevent these cycles between Book and Written. A side note, transient has nothing to do with the persistence but the serialization. JPA works with the @Transient annotation.

public class Book implements Serializable {

    @OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
    @JsonBackReference
    private Set<Written> written= new HashSet<>();

    ...
}
public class Written implements Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = "idBook")
    @JsonManagedReference
    private Book book;

    ...
}

Important: Don't send database entities through REST (probably what you are up to do). Better create a DAO object without bidirectional relationship and map entities into DAOs. There are several libraries able to do that: I highly recommend MapStruct, however ModelMapper is also an option. If there is a lower number of such entities, using constructors/getters/setters would be enough.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • Oh thank you so much, but the breeding behavior is not quite what is expected. I can now retrieve the books and their information (no more infinite recursion errors), but no trace of the author or additional fields stored in Written. Any tips ? – nicooooo Oct 09 '20 at 12:00
  • @nicooooo Use a mapping library to map only the "first" layer of books without a further reference to a writter. – Nikolas Charalambidis Oct 09 '20 at 12:35
  • 1
    I apologize in advance, but this is my first app in this domain and I have no idea how to do it – nicooooo Oct 09 '20 at 12:54
  • That's fine. For sake of simplicity, don't use the bidirectional relationships unless you *really* need them. For more information about entity-to-dto-mapping, search this term on Google: MapStruct entity to dto and you find plenty of tutorials to start. – Nikolas Charalambidis Oct 09 '20 at 13:19