我现在正在与Hibernate实体和JSON斗争,尽管有很多关于该对象的问题,但我还是无法在存在循环依赖的情况下进行序列化。我和Gson以及杰克逊都试过了,但我没有取得很多进展。 这是我的对象的摘录。 这是"父母"类。
@Entity
public class User extends RecognizedServerEntities implements java.io.Serializable
{
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Thread> threads = new HashSet<Thread>(0);
//...other attributes, getters and setters
}
这是&#34;孩子&#34;类
@Entity
@Table(name = "thread")
public class Thread extends RecognizedServerEntities implements java.io.Serializable
{
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author", nullable = true)
private User user;
//...other attributes, getters and setters
}
我写了一个简单的类来测试gson和jackson的特性;如上所述,他们都提出异常。
public class MyJsonsTest
{
private static User u;
public static void main(String[] args)
{
u = new User("mail", "password", "nickname", new Date());
u.setId(1); // Added with EDIT 1
// testGson();
testJackson();
}
private static void testJackson()
{
Thread t = new Thread("Test", u, new Date(), new Date());
t.setId(1); // Added with EDIT 1
u.getThreads().add(t);
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try
{
mapper.writeValue(new File("result.json"), u);
}
catch {/[various exceptions catched, but a JsonMappingException was thrown]}
}
private static void testGson()
{
Gson gson = new Gson();
System.out.println(u.toString());
System.out.println(gson.toJson(u, User.class));
Thread t = new Thread("Test", u, new Date(), new Date());
u.getThreads().add(t);
//This raise an exception overflow
System.out.println(gson.toJson(u, User.class));
}
}
为了解决这个问题,在杰克逊方面,我试图使用这个注释
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
用户和线程类上的。但是,它并没有解决问题。 在gson方面,我读到了GraphAdapterBuilder类,但我无法正确使用它。我找不到任何jar,所以我从here复制/粘贴了源代码。但是,此行存在编译时错误
private final ConstructorConstructor constructorConstructor = new ConstructorConstructor();
因为ConstructorConstructor()未定义;正确的语法应该是
ConstructorConstructor(Map<Type>, InstanceCreator<?> instanceCreators)
那么,这个问题是否有明确的解决方案?显然,我无法使用transient
变量。
我终于找到了杰克逊的问题。在测试类中,我忘记初始化id字段(在实际场景中,它由数据库初始化),这就是异常的原因。当我最终设置id时,一切正常。这是输出
{
"id" : 1,
"email" : "mail",
"password" : "password",
"nick" : "nickname",
"registeredDate" : 1414703168409,
"threads" : [ {
"id" : 1,
"thread" : null,
"user" : 1,
"title" : "Test",
"lastModifiedDate" : 1414703168410,
"createdDate" : 1414703168410,
"messages" : [ ],
"threads" : [ ]
} ],
"messages" : [ ]
}
答案 0 :(得分:3)
<强>杰克逊强>
如上所述,我能够使用
解决问题@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=MyEntity.class)`
建议here的每个实体。
scope
属性是必要的,以确保名称&#34; id&#34;在范围内是独一无二的。实际上,如果没有scope
属性,您可以看到here,它会抛出一个例外
com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey@3372bb3f] (through reference chain: ParentEntity["children"]->java.util.ArrayList[0]->ChildEntity["id"])
...stacktrace...
Caused by: java.lang.IllegalStateException: Already had POJO for id (java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey@3372bb3f]
...stacktrace...
<强> GSON 强>
我仍然没有找到一种干净的方法来序列化循环依赖。
答案 1 :(得分:2)
在处理循环依赖时,您需要构建父子JSON层次结构,因为编组必须从根级联到最内层子级。
对于双向关联,当Parent具有一对多子项集合且子项具有对Parent的多对一引用时,您需要使用{{3注释多对一端。 }}:
@Entity
@Table(name = "thread")
public class Thread extends RecognizedServerEntities implements java.io.Serializable
{
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@org.codehaus.jackson.annotate.JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author", nullable = true)
private User user;
//...other attributes, getters and setters
}
这样您将不再具有Json序列化时间循环依赖。
答案 2 :(得分:1)
我是用这种方式使用 org.codehaus.jackson.annotate.JsonManagedReference 和 org.codehaus.jackson.annotate.JsonBackReference 完成的...
看看我如何使用 @JsonManagedReference
@Id
@TableGenerator(name="CatId", table="catTable",pkColumnName="catKey",pkColumnValue="catValue", allocationSize=1)
@GeneratedValue(strategy=GenerationType.TABLE, generator="CatId")
@Column(name = "CategId", unique = true, nullable = false)
private long categoryId;
private String productCategory;
@JsonManagedReference("product-category")
@OneToMany(targetEntity=ProductDatabase.class,mappedBy="category", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<ProductDatabase> catProducts;
然后在另一端使用 @JsonBackReference ,如下所示。
@Id@GeneratedValue
private int productId;
private String description;
private int productPrice;
private String productName;
private String ProductImageURL;
@JsonBackReference("product-category")
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "CategId")
private Category category;
只需应用这些注释并检查它是否适合您。
答案 3 :(得分:0)
将Hibernate POJO序列化为客户端并不是一个好的设计。因为您可能会将一些数据发送到客户端位置,而这些位置并未授权他查看。您应该创建客户端POJO并将数据从休眠POJO复制到您要发送给客户端的客户端POJO。如果您不想这样做,可以使用@JsonIgnore或热切地获取所有数据。