Reputation: 33
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
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