使用Spring MVC,Jackson和Hibernate序列化具有多对多关系的对象

时间:2014-04-18 14:02:59

标签: json hibernate rest spring-mvc jackson

我很难弄清楚如何在我的Web应用程序中正确定义和注释模型,以便它们可以有效地用于Web界面和REST Web服务。以下是给我带来麻烦的关系的简化版本:

发布模型:

@Entity
@Table(name = "POST")
public class Post implements Serializable {

    @Id
    @Column(name = "POST_ID")
    @GeneratedValue(strategy-GenerationType.AUTO)
    private Integer postId;

    @Column(name = "POST_BODY")
    private String postBody;

    @ManyToMany(fetch = FetchType.EAGER)
    @Cascade({CascadeType.SAVE_UPDATE})
    @JoinTable(name = "POST_TAGS", 
            joinColumns={@JoinColumn(name="POST_ID")},
            inverseJoinColumns={@JoinColumn(name="TAG_ID")})
    private Set<Tag> tags = new HashSet<Tag>();

    //Getters and setters...
}

标记模型

@Entity
@Table(name = "TAG")
public class Tag implements Serializable {

    @Id
    @Column(name = "TAG_ID")
    @GeneratedValue(strategy-GenerationType.AUTO)
    private Integer tagId;

    @Column(name = "TAG_NAME")
    private String tagName;

    @ManyToMany(fetch = FetchType.EAGER, mappedBy="tags")
    private Set<Post> posts = new HashSet<Post>();

    //Getters and setters...
}

我有一个用于获取所有帖子的Web服务控制器方法和一个用于获取所有标记的方法。理想情况下,每个方法都应返回目标类的列表和引用的类。例如:

[{
    postId: 1,
    postBody: "Hello world!",
    tags: [{
        tagId: 1,
        tagName: "hello"
    },{
        tagId: 2,
        tagName: "message"
    }]
}, { 
    postId: 2,
    ....
}]

但我最终看到的是无限递归,因为每个帖子的返回标签都会检索其关联的帖子,这些帖子会将其关联的标签等检索到无穷大。我尝试在我的类的getter方法上使用@JsonIgnore,但后来我不再获取引用的对象。我已经尝试使用@JsonIdentityInfo向我的对象添加ID,但后来我仍然得到递归,但仅限于检索到的对象之间的所有引用都已耗尽的程度。我似乎找不到任何解释如何处理这些情况的文档,这似乎是一个常见的情况。

3 个答案:

答案 0 :(得分:0)

您可以使用杰克逊的注释(@JsonManagedReference@JsonBackReference)。

检查http://wiki.fasterxml.com/JacksonFeatureBiDirReferences

或者您可以编写自己的序列化程序并跟踪已经序列化的内容,这就是我在使用Gson而不是Jackson时最终要做的事情。

答案 1 :(得分:0)

我写了jackson hibernate模块的详细用法(https://github.com/FasterXML/jackson-datatype-hibernate) 抱歉我的英语不好。

如下面的OP帖子所述 - 如果我们使用默认的jackson + jackson-databind模块 - 我们遇到了多对多序列化的麻烦。如果我们不使用@JsonIgnore + @JsonIdentityInfo注释,那就是“无限循环”。如果我们同时使用它们,它就是“ID作为结果”。

解决问题的最佳方法 - 使用模块。 1.添加maven dep

<dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-hibernate4</artifactId>
      <version>${jackson.version}</version>
</dependency>
  1. 创建并使用自定义ObjectMapper
  2. <强> HibernateAwareObjectMapper.java

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
    
    public class HibernateAwareObjectMapper extends ObjectMapper {
    
        public HibernateAwareObjectMapper() {
            registerModule(new Hibernate4Module());
        }
    }
    

    3。在spring XML config

    中使用此映射器
    <mvc:annotation-driven>
            <mvc:message-converters>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper">
                        <bean class="path.HibernateAwareObjectMapper"/>
                    </property>
                </bean>
            </mvc:message-converters>
        </mvc:annotation-driven>
    
    1. 向实体字段添加注释。
    2. 发布实体不需要注释。

      标记实体添加@JsonIgnore注释以修复“无限循环”错误。

      我们也可以将EAGER改为LAZY fetchtype。并通过ObjectMapper获取规则获取FORCE_LAZY_LOADING 更多信息在javadoc(http://fasterxml.github.io/jackson-datatype-hibernate/javadoc/h4-2.4/com/fasterxml/jackson/datatype/hibernate4/Hibernate4Module.Feature.html#FORCE_LAZY_LOADING)或github上的代码

答案 2 :(得分:0)

在代码中再添加两个注释:

@Entity
@Table(name = "POST")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "postId" )
public class Post implements Serializable {

    @Id
    @Column(name = "POST_ID")
    @GeneratedValue(strategy-GenerationType.AUTO)
    private Integer postId;

    //Rest of the class
}

并且:

@Entity
@Table(name = "TAG")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "tagId" )
public class Tag implements Serializable {

    @Id
    @Column(name = "TAG_ID")
    @GeneratedValue(strategy-GenerationType.AUTO)
    private Integer tagId;

    //Rest of the class
}

如Jackson文档所述:

  

在实践中,这是通过将第一个实例序列化为完整的对象和对象标识以及将对对象的其他引用作为参考值来实现的。