无限递归:在中间表上使用Jackson

huangapple go评论76阅读模式
英文:

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 = &quot;Author&quot;)
public class Author implements Serializable {
/...
 
    @Id
    @GeneratedValue
    private int id;

    @OneToMany(mappedBy = &quot;author&quot;, cascade = CascadeType.ALL)
    private Set&lt;Written&gt; written= new HashSet&lt;&gt;();
/...
}

@Entity
@Table(name = &quot;Book&quot;)
public class Book implements Serializable{
/...

	@Id
    @GeneratedValue
    private int id;

    @OneToMany(mappedBy = &quot;book&quot;, cascade = CascadeType.ALL)
    private Set&lt;Written&gt; written= new HashSet&lt;&gt;();
/...
}


public class Written implements Serializable {

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

    @Id
    @ManyToOne
    @JoinColumn(name = &quot;idBook&quot;)
    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]-&gt;com.exampleAPI.api.model.Book[&quot;written&quot;])]

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 注解来防止 BookWritten 之间出现循环引用。另外需要注意的是,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 = &quot;book&quot;, cascade = CascadeType.ALL)
    @JsonBackReference
    private Set&lt;Written&gt; written= new HashSet&lt;&gt;();

    ...
}
public class Written implements Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = &quot;idBook&quot;)
    @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.

huangapple
  • 本文由 发表于 2020年10月9日 18:17:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/64278070.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定