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>

这样就完美的解决了这个问题。

时间: 2024-10-04 17:34:35

JPA一对多循环引用的解决的相关文章

在mvc返回JSON时出错:序列化类型为“System.Data.Entity.DynamicProxies.Photos....这个会的对象时检测到循环引用 的解决办法

在MVC中返回JSON时出错,序列化类型为“System.Data.Entity.DynamicProxies.Photos....这个会的对象时检测到循环引用. public ActionResult GetSdirsbyFdirid(int id) { // db.Configuration.LazyLoadingEnabled = false; db.Configuration.ProxyCreationEnabled = false; List<Seconddirectory> lis

Block的使用及循环引用的解决

Block是一个很好用的东西,这篇文章主要来介绍:1.什么是Block?2.Block的使用?3.Block的循环引用问题及解决. 上面三点应该说是一个很大的问题,目前因为在做项目,我先仅就第三点做叙述,前两点等空闲的时候我再做补充. 1. 2. 3.Block的循环引用问题及解决. 首先我们需要明确的是,一个对象的Block属性是使用copy来修饰,当Block被copy时,会对block中用到的对象产生强引用(ARC)或者引用计数加一(MRC).当我们使用Block时,如果Block方法又引

Swift类实例与循环引用的解决

代码-情形1: // 两个属性的值都允许为nil,这种场景最适合用弱引用来解决 class Person { let name: String init(name: String) { self.name = name } // 弱引用 var apartment: Apartment? deinit { print("\(name) is being deinitialized") } } class Apartment { let number: Int init(number:

C#项目间循环引用的解决办法,有图有真相

程序间的互相调用接口,c#禁止互相引用,海宏软件,20160315 /// c#禁止互相引用,如果项目[订单]中有一个orderEdit单元,要在项目[进销存]中不能直接引用,所以定义这一标准接口. /// 1:定义标准接口在公共单元中. /// 2:在主程序中引用订单和进销存单元. /// 3:主程序中重写接口的虚方法,调用[订单]项目的功能 /// 4:主程序启动时,创建调用实例,把变量写到公共静态变量 /// 5:进销存项目通过公共变量调用 比较简单就解决了.

EF中Json序列化对象时检测到循环引用的解决办法

第一种方法:使用Newtonsoft.Json中的方法注释,在Json序列化的时候忽略导航属性 例:using Newtonsoft.Json; public class Users { public int Id { get; set; } public string LoginId { get; set; } public string LoginPwd { get; set; } [JsonIgnore] public virtual ICollection Roles { get; se

iOS Block循环引用

前言 本篇文章精讲iOS开发中使用Block时一定要注意内存管理问题,很容易造成循环引用.本篇文章的目标是帮助大家快速掌握使用block的技巧. 我相信大家都觉得使用block给开发带来了多大的便利,但是有很多开发者对block内存管理掌握得不够好,导致经常出现循环引用的问题.对于新手来说,出现循环引用时,是很难去查找的,因此通过Leaks不一定能检测出来,更重要的还是要靠自己的分析来推断出来. 声景一:Controller之间block传值 现在,我们声明两个控制器类,一个叫ViewContr

ios之block循环引用

在 iOS 4.2 时,苹果推出了 ARC 的内存管理机制.这是一种编译期的内存管理方式,在编译时,编译器会判断 Cocoa 对象的使用状况,并适当的加上 retain 和 release,使得对象的内存被合理的管理.所以,ARC 和 MRC 在本质上是一样的,都是通过引用计数的内存管理方式. 然而 ARC 并不是万能的,有时为了程序能够正常运行,会隐式的持有或复制对象,如果不加以注意,便会造成内存泄露!今天就列举几个在 ARC 下容易产生内存泄露的点,和各位童鞋一起分享下. block 系列

iOS Block循环引用精讲

前言 循环引用就是当self 拥有一个block的时候,在block 又调用self的方法.形成你中有我,我中有你,谁都无法将谁释放的困局.又或者解决方法简而言之就一句话的事情:__weak typeof (self) weakSelf = self; 本篇文章精讲iOS开发中使用Block时一定要注意内存管理问题,很容易造成循环引用.本篇文章的目标是帮助大家快速掌握使用block的技巧. 我相信大家都觉得使用block给开发带来了多大的便利,但是有很多开发者对block内存管理掌握得不够好,导

Wcf序列化的循环引用问题1

1.Wcf数据契约序列化,使用的类DataContractSerializer 默认如果类不指定[DataContract],则序列化类的所有字段,并且在出现循环引用的时候回抛出异常,服务终止 msdn文档说明:https://msdn.microsoft.com/library/system.runtime.serialization.datacontractserializer.aspx /* * Wcf 数据契约序列化使用“DataContractSerializer”,底层是xml序列化