英文:
Infinite recursion with Jackson on intermediate table
问题
我正在使用Spring Boot、Jackson和Hibernate来创建一个API。Hibernate用于连接MySQL数据库。我了解到一些良好的实践,但我在一个特定的问题上陷入了困境。我有一个n:m的关系,其中包含一个额外的字段。
例如:Author(id,...)-> Written(idAuthor,idBook,date)<- Book(id,...)
我了解如何映射传统的n:m关系,但这种技术这次对我不适用。
为此,我在互联网上找到了一个显示解决方案的来源:在我的代码中创建一个包含Author类型对象和Book类型对象以及我的附加字段的中间类。
@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 ....
}
这是一个双向链接。使用这段代码,我会得到一个无限递归错误:
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"])]
我尝试过在Written类上使用@JsonIgnore
、@JsonManagedReference
和@JsonBackReference
,还尝试过使用transient
关键字,但都没有奏效。
我找不到任何可以帮助我解决这个问题的互联网来源,文档中也没有针对这种特殊情况的说明。
有人能帮助我吗?
英文:
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?
答案1
得分: 1
当出现未处理的双向关系时,Jackson会面临无限递归问题。
> 我尝试在Written类上使用@JsonIgnore、@JsonManagedReference和@JsonBackReference
你需要分别使用 @JsonManagedReference
和 @JsonBackReference
注解来防止 Book
和 Written
之间出现循环引用。另外需要注意的是,transient
与持久化无关,而与序列化有关。JPA 使用 @Transient
注解。
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;
...
}
重要提示: 不要通过REST发送数据库实体(可能是你要做的事情)。最好创建一个没有双向关系的DAO对象,并将实体映射到DAO中。有几个库可以实现这一点:我强烈推荐使用 MapStruct,不过 ModelMapper 也是一个选择。如果这类实体较少,使用构造函数/Getter/Setter 也足够了。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论