JPA一对多循环引用的解决
Posted 默默爬坑中
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JPA一对多循环引用的解决相关的知识,希望对你有一定的参考价值。
说是解决,其实不是很完美的解决的,写出来只是想记录一下这个问题或者看一下有没有哪位仁兄会的,能否知道一二。
下面说说出现问题:
问题是这样的,当我查询一个一对多的实体的时候,工具直接就爆了,差不多我就猜到是哪里死循环了,最后等了好久,查看原因,果然是堆溢出,再然后是jsckson的错误。那么必然是序列化的问题了。
这是jackson的错误:
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at java.lang.ClassLoader.loadClass(ClassLoader.java:412)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1617)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1547)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:691)
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)
这是循环引用的错误:
严重: Servlet.service() for servlet [springDispatcherServlet] in context with path [/Shop] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError) (through reference chain: com.web.module.index.model.entity.Account["user"]->com.web.module.index.model.entity.User["accounts"]->org.hibernate.collection.internal.PersistentSet[0]->com.web.module.index.model.entity.Account["user"]->com.web.module.index.model.entity.User["accounts"]->org.hibernate.collection.internal.PersistentSet[0]->com.web.module.index.model.entity.Account["user"]->com.web.module.index.model.entity.User["accounts"]->org.hibernate.collection.internal.PersistentSet[0]->com.web.module.index.model.entity.Account["user"]->com.web.module.index.model.entity.User["accounts"]->org.hibernate.collection.internal.PersistentSet[0]->com.web.module.index.model.entity.Account["user"]->com.web.module.index.model.entity.User["accounts"]->org.hibernate.collection.internal.PersistentSet[0]->com.web.module.index.model.entity.Account["user"]->com.web.module.index.model.entity.User["accounts"]->org.hibernate.collection.internal.PersistentSet[0]->com.web.module.index.model.entity.Account["user"]->com.web.module.index.model.entity.User["accounts"]->org.hibernate.collection.internal.PersistentSet[0]-
j。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。还有很多的相同的错误
下面是两个实体:
User.java:
package com.web.module.index.model.entity; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.hibernate.validator.constraints.NotEmpty; import com.fasterxml.jackson.annotation.JsonIgnore; @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name="user") @Entity public class User implements Serializable{ /** * */ private static final long serialVersionUID = 1L; @XmlElement @Id private String id; /** * validate适用于springmvc */ @XmlElement //@NotEmpty private String name; @JsonIgnore @OneToMany(mappedBy="user",targetEntity=Account.class,fetch=FetchType.EAGER) private Set<Account> accounts=new HashSet<Account>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Set<Account> getAccounts() { return accounts; } public void setAccounts(Set<Account> accounts) { this.accounts = accounts; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", accounts=" + accounts + "]"; } }
Account.java:
package com.web.module.index.model.entity;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
public class Account implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
private String id;
private String code;
private String password;
@JsonIgnore
@JoinColumn(name="user_id")
@ManyToOne(targetEntity=User.class,fetch=FetchType.EAGER)
private User user;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Account [id=" + id + ", code=" + code + ", password="
+ password + ", user=" + user + "]";
}
}
后来去网上看了一下,这个问题很多人遇到。解决方案也有很多.
1.在关联的实体上面设置@JsonIgnore,这个注解的意思是表示在序列化的时候,忽略这个属性.但是我现在的逻辑是在页面中必须使用到这个关联实体中的属性,所以就不能这么做了,不然在页面中是取不出这个数据的。
Uncaught TypeError: Cannot read property ‘name‘ of undefined(1,2都会出现)
2.采用单向多对一的形式,这样就不会出现循环的问题,这个确实是个方案,但是如果在一的那边需要使用到多的这边的话,就不好搞了。所以感觉还是不是很满意。
3.后来想了想,既然是这样,要不我在一的那边使用@JsonIgnore吧。目前在页面中没使用。其实这个是第二个是差不多的,有点不同的是除了页面展示的时候不能够显示多的那面的数据,在其他的业务中还是能够使用的。这也是我在前面说不是很满意的解决办法。
4.第四种解决就是前面的3差不多,当我们使用多的一边的时候,可以正确的显示,但是在我们使用一的那一端的时候,我们可以使用List自己拼装,有点像下面的代码:
@RequestMapping(value="result/{id}",method=RequestMethod.GET) public @ResponseBody List<?> result(@PathVariable("id") String id){ System.out.println(id); List<Map<String,Object>> list=Lists.newArrayList(); //Map<String,Object> map=new HashMap<String,Object>(); Map<String,Object> map=null; Random r=new Random(); DecimalFormat dfmt=new DecimalFormat("#,###.00"); for(int i=0;i<4;i++){ int price=r.nextInt(10)+1; int number=r.nextInt(100000)+10000; map=new HashMap<String,Object>(); map.put("tradegoods", "煤"+i); map.put("units", "顿"); map.put("consumer", "XX物流"+id); map.put("unitPrice", dfmt.format(price)); map.put("number", dfmt.format(number)); map.put("count", dfmt.format(price*number)); list.add(map); } //设置日期格式 return list; }
这样jackson序列化的时候,就不会出错了,而且使用起来就不用像A.B.name这样了,而且使用起来也更加的简单。我们在JS里面就可以这样使用:
if(id!=""&&id){ $.ajax({ type: ‘GET‘, url: $ctx + ‘/example/demo/result/‘+id, dataType: ‘json‘, success: function(data) { for(var i=0;i<data.length;i++){ data[i].num=i+1; } //alert(JSON.stringify(data)); viewModel.result(data); $(".notice-hide").show(); $(".notice-show").hide(); }, error: function(req, textStatus, errorThrown){ } });
html:
<tbody data-bind="foreach: result"> <tr> <td data-bind="text:num"></td> <td data-bind="text:tradegoods"></td> <td data-bind="text:units"></td> <td data-bind="text:consumer"></td> <td data-bind="text:unitPrice" class="format_"></td> <td data-bind="text:number" class="format_"></td> <td data-bind="text:count" class="format_"></td> </tr> </tbody>
这样就完美的解决了这个问题。
以上是关于JPA一对多循环引用的解决的主要内容,如果未能解决你的问题,请参考以下文章