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 替换已经看到的引用的问题。
我将 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+,可以,只需参阅
作为参考,johnzon 支持通过映射器 (tomee 7) 和 johnzon.deduplicateObjects 属性(tomee 8、meecrowave 等...)上的 deduplicateObjects 选项处理引用。这解决了用 jsonpointer 替换已经看到的引用的问题。