nicooooo
nicooooo

Reputation: 33

Infinite recursion with Jackson on intermediate table

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?

Upvotes: 2

Views: 966

Answers (1)

Nikolas
Nikolas

Reputation: 44456

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.

Upvotes: 1

Related Questions