杰克逊 JSON、Spring MVC 4.2 和 Hibernate JPA 问题的无限递归

Posted

技术标签:

【中文标题】杰克逊 JSON、Spring MVC 4.2 和 Hibernate JPA 问题的无限递归【英文标题】:Infinite Recursion with Jackson JSON, Spring MVC 4.2 and Hibernate JPA issue 【发布时间】:2015-11-28 01:29:33 【问题描述】:

当 Spring MVC 尝试通过 jackson 2.6.1 将具有双向关联的 JPA 对象转换为 JSON 时,我不断得到

org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (***Error)

第一个实体是:

import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.Entity;
import javax.persistence.Table;
...

@Entity
@Table(name = "user")
public class User implements java.io.Serializable 
    private Integer userId;
    @JsonManagedReference
    private UserClass userClass;

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "userId", unique = true, nullable = false)
public Integer getUserId() 
    return this.userId;


@ManyToOne()
@JoinColumn(name = "userClassId")

public UserClass getUserClass() 
    return this.userClass;

.......

第二个是:

 import com.fasterxml.jackson.annotation.JsonManagedReference;
 import javax.persistence.Entity;
 import javax.persistence.Table;
 ...

@Entity
@Table(name = "user_class")
public class UserClass implements java.io.Serializable 

private Integer userClassId;
@JsonBackReference
private List<User> users = new ArrayList<User>(0);

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "userClassId", unique = true, nullable = false)
public Integer getUserClassId() 
    return this.userClassId;



@OneToMany(fetch = FetchType.LAZY, mappedBy = "userClass")
public List<User> getUsers() 
     return this.users;

这里是依赖项:

<dependencies>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>4.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.8.2.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aop</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <scope>provided</scope>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.1</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.6.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.6.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-hibernate4</artifactId>
            <version>2.6.1</version>
        </dependency>



        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.3.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.1.3.Final</version>
        </dependency>

        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-core</artifactId>
            <version>3.0.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-jsp</artifactId>
            <version>3.0.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-api</artifactId>
            <version>3.0.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-template</artifactId>
            <version>3.0.5</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.12</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>


    </dependencies>

我使用“@JsonIgnore”、“@JsonIdentityInfo”和“XmlTransient”来防止递归,但它们都不起作用,服务器报告此错误。有人可以帮我吗?

谢谢。

编辑: 和堆栈跟踪:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (***Error) (through reference chain:
sss.com.model.UserClass["users"]->org.hibernate.collection.internal.PersistentBag[0]->sss.com.model.User["userClass"]->sss.com.model.UserClass["users"]->
org.hibernate.collection.internal.PersistentBag[0]->sss.com.model.User["userClass"]->sss.com.model.UserClass["users"]->org.hibernate.collection.internal.PersistentBag[0]->sss.com.model.User["userClass"]->sss.com.model.UserClass["users"]... .
 nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (***Error) (through reference chain: 
sss.com.model.UserClass["users"]->org.hibernate.collection.internal.PersistentBag[0]->sss.com.model.User["userClass"]->sss.com.model.UserClass["users"]->
org.hibernate.collection.internal.PersistentBag[0]->sss.com.model.User["userClass"]->sss.com.model.UserClass["users"]->org.hibernate.collection.internal.PersistentBag[0]->sss.com.model.User["userClass"]->sss.com.model.UserClass["users"]... . with root cause
 java.lang.***Error
    at java.io.IOException.<init>(IOException.java:58)
    at com.fasterxml.jackson.core.JsonProcessingException.<init>(JsonProcessingException.java:25)
    at com.fasterxml.jackson.core.JsonProcessingException.<init>(JsonProcessingException.java:41)
    at com.fasterxml.jackson.databind.JsonMappingException.<init>(JsonMappingException.java:143)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:149)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:111)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:24)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:656)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:656)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:149)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:111)
    at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:24)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:656)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
...

已解决: 在我的原始实体中,我有 5 个双向关系,但只是,我被注释了一个关系。原因是tomcat没有在结果页面打印异常,我没有检查tomcat日志文件。

【问题讨论】:

发布异常的完整堆栈跟踪。 添加了堆栈跟踪。谢谢 一个免费的建议,不要使用你的 JPA 实体来生成 JSON,创建一组用于前端/服务和数据模型之间的 DTO,它会让你的生活更轻松。 【参考方案1】:

只需输入@JsonIgnore

@JsonIgnore
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userClass")
public List<User> getUsers() 
     return this.users;

这将忽略延迟加载对象并解决您的问题。

更新

根据Udara Seneviratneanswer,您可以通过@JsonManagedReference, @JsonBackReference处理bi-directional relationship

【讨论】:

这对我也有用。但我不明白为什么在某些时候某些数据对象会发生无限递归。 @Brain Spring Hibernate Transaction session 在@Transactional 块结束后消失,当jackson 尝试从代理休眠对象延迟加载用户时,由于会话已经结束而抛出异常。 谢谢。这就说得通了。那么使用 Hibernate.initialize(...) 来防止这个错误有帮助吗? 这将忽略响应中的特定字段(List)。以下链接有一个解决方案baeldung.com/…【参考方案2】:

@JsonIgnore 只是忽略结果中的注释字段。如果您还需要在结果中包含异常导致的字段,请根据以下教程使用@JsonManagedReference、@JsonBackReference。 http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

如果一个类有多个双向关系,最好使用@JsonIdentityInfo

【讨论】:

【参考方案3】:

可以通过多种方式完成

从某个类中删除 getter 方法。

在其中一个类中使用@JsonIgnore (问题:在上述两种情况下,它都会忽略结果中的字段。)

如果你使用下面的注释,它也会包含该字段

@JsonManagedReference,@JsonBackReference @JsonIdentityInfo( 生成器 = ObjectIdGenerators.PropertyGenerator.class, 属性 = "id")

【讨论】:

【参考方案4】:

尝试在manytoone引用端上方添加@XmlTransient注解,

import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.Entity;
import javax.persistence.Table;
 ...

@Entity
@Table(name = "user")
public class User implements java.io.Serializable 
private Integer userId;
@JsonManagedReference
private UserClass userClass;

 @Id
 @GeneratedValue(strategy = IDENTITY)
 @Column(name = "userId", unique = true, nullable = false)
 public Integer getUserId() 
  return this.userId;
 

 @XmlTransient
 @ManyToOne()
 @JoinColumn(name = "userClassId")

  public UserClass getUserClass() 
  return this.userClass;

 .......

【讨论】:

我添加了这个注释(@javax.xml.bind.annotation.XmlTransient)但是,我再次看到了那个错误。感谢您回答我的问题。 好的。据我了解,这个问题提供了循环参考。所以如果你使用 XmlTransient 注解。这将忽略响应中的字段。此注解需要添加使用多点注解的所有实体。还尝试删除 json 相关的注释(JsonManagedReference、JsonIgnore、JsonIdentityInfo 等)

以上是关于杰克逊 JSON、Spring MVC 4.2 和 Hibernate JPA 问题的无限递归的主要内容,如果未能解决你的问题,请参考以下文章

杰克逊 2.0 和 Spring 3.1

杰克逊 2.0 和 Spring 3.1

❤️‍Spring全家桶从入门到大神--mvc 常见知识点

Spring Security 4.2 中的 StrictHttpFirewall 与 Spring MVC @MatrixVariable

Spring MVC 4.2+ CORS 返回 403

热到让杰克逊在 Spring Boot REST API 中按需使用蛇案例/骆驼案例?