Hibernate 和 TomEE+ 双向关联导致 json 递归

Hibernate and TomEE+ bidirectional association causes json recursiveness

我将 TomEE 9.0.12 与 Hibernate 5.3.7 一起使用,由于 json 递归性,我目前遇到了 Whosebug 异常。请注意,我没有使用 jackson,理想情况下也不想使用,而是 TomEE+ 附带的标准:org.apache.johnzon.

异常:

Caused by: java.lang.WhosebugError
    at org.apache.johnzon.mapper.Mappings.findOrCreateClassMapping(Mappings.java:340)
    at org.apache.johnzon.mapper.MappingGeneratorImpl.doWriteObjectBody(MappingGeneratorImpl.java:240)

这是我的 User.java class:

import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * A user
 */
@Entity
@Table(name = "users")
public class User implements Serializable {

    private static final long serialVersionUID = -9012412584251217L;

    // TODO: 16/12/2018 they keep nesting each other: { user { posts { user { posts } } }  }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "username")
    private String username;

    @Column(name = "email")
    private String email;

    @Column(name = "password")
    private String password;

    @Column(name = "salt")
    private String salt;

    @OneToMany(mappedBy = "user")
    private Set<Post> posts = new HashSet<>();

    /**
     * Constructs an empty user (for persistence purposes)
     */
    public User() {}

    // Setters and getters removed for convenience
}

这是我的 Post.java

import javax.persistence.*;
import java.io.Serializable;

/**
 * Project created by ExpDev
 */
@Entity
@Table(name = "posts")
public class Post implements Serializable {

    private static final long serialVersionUID = -9887234238952234L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @Column(name = "data")
    private String data;

    /**
     * Constructs an empty post (for persistence purposes)
     */
    public Post() {}

    // Setters and getters removed for convenience
}

问题

问题是他们会不断地互相提及。用户将引用 posts,后者将引用回用户,用户将再次引用 posts-- 如此恶性循环继续。

我知道我可以在 User.java 中的 "user" 上使用 org.apache.johnzon 的注释 @JsonbTransient 来使其不序列化,但这使得它永远不会显示(我想要,但有条件)。我为什么要这样?

因为,假设访问了 GET /users/1,1 是用户的 ID,我想显示所有用户的 post(或至少是他们的 ID),但是不希望它返回给用户(嵌套用户 inside post inside user)。

但是,如果 GET /api/posts/3,3 是 id,我希望它显示创建 post 的用户,而不是在用户内部再次显示 post。

所以基本上,注释必须在某种程度上是有条件的,并且是特定的。目前,这就是我检索和显示用户的方式(框架在访问该方法时自动将返回的对象转换为 JSON)。

@GET
@Path("/{id}")
public User get(@PathParam("id") Long id) {
    // Find the user and return them
    return HiberUtil.getSession().get(User.class, id);
}

提前致谢!我已经搜索了 5 个小时,但没有任何解决方案或阅读 material 解决了我的问题。

我通过从 TomEE+ 转移到 Spring 框架(带有嵌入式 Tomcat 服务器)来解决这个问题,它使用 Jackson 作为默认值而不是“org.apache.johnzon。有了这个, 我能够使用有用的注释:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
@JsonIdentityReference(alwaysAsId=true)
private List<Post> posts;

这样 JSON 看起来像这样:

{
    "username":"test",
    "posts": [2, 5, 6, 8, 2]
}

Post 中的用户也是如此,当然它也标有这些注释。这正是我想要的,也是我使用的前端 Web 框架 EmberJS 和许多类似框架期望返回数据的方式。所以这对我来说是完美的。此外,在使用嵌入式 Tomcat 服务器玩 Spring 一段时间后,我也更喜欢它(我也使用过 Glassfish,但不喜欢它)。

重要的是要注意,修复不是因为我搬到了 Spring,而是因为我改用了 Jackson。如果您仍想使用 TomEE+,可以,只需参阅 了解如何在 TomEE 中使用 jackson 而不是 johnzon。

作为参考,johnzon 支持通过映射器 (tomee 7) 和 johnzon.deduplicateObjects 属性(tomee 8、meecrowave 等...)上的 deduplicateObjects 选项处理引用。这解决了用 jsonpointer 替换已经看到的引用的问题。