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@Idprivate String id;/*** validate適用于springmvc*/@XmlElement//@NotEmptyprivate 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;}@Overridepublic 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;@Idprivate 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;}@Overridepublic 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>

這樣就完美的解決了這個問題。

?5添加Filter的方式進行動態的過濾屬性 ,上面的解決方法還是或多或少的影響到我們正常的使用類,下面說的方法是不會影響放到原有的類的。

jsckson的ObjectMapper有一個

    public final void addMixInAnnotations(Class<?> target, Class<?> mixinSource){_mixInAnnotations.put(new ClassKey(target), mixinSource);}public final Class<?> findMixInClassFor(Class<?> cls) {return (_mixInAnnotations == null) ? null : _mixInAnnotations.get(new ClassKey(cls));}public final int mixInCount() {return (_mixInAnnotations == null) ? 0 : _mixInAnnotations.size();}

這樣的方法,這個方法的使用就要結合JsonIgnoreProperties注解一起來進行使用。我們需要定義一個接口,這個接口的作用是用來專門的過濾屬性的。

還是針對上面的例子,我們要解決問題的話 ?,我們需要在定義一個接口:

package com.hotusm.jackson;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;@JsonIgnoreProperties(ignoreUnknown=true,value={"user"})
public interface AccountFilter {
}

這個接口非常簡單,就是一個注解,注解其中的value就是表示的是我們需要將那些屬性給忽略掉,增加了這么一個接口后,我們就可以使用上面提到的方法。

objectMapper.addMixInAnnotations(Account.class, AccountFilter.class);

之后再使用這個objectmapper的時候,在account類上面的user就不會被忽略掉了,通過這種方式,我們不用修改原來類的任何地方。但是這種方式需要我們重新創建一個接口,所以下面一種就是解決這種每次都要創建的痛苦了。

6.利用自定義注解的方式來進行過濾,這種方式也是看到其他人使用,感覺非常好,也就做一個簡單的總結。

大概的講一下思路

? ? 1.還是使用addMixInAnnotations方法,但是不需要我們每次都創建一個接口而是采用全注解的形式來。也許會很奇怪,前面的方法命名

是傳入兩個class啊 ,我們不手動創建的話,那該怎樣的去調用呢。這里我們使用字節碼技術Javassist來動態的創建class。

? ?2.大概的思路就是我們自定義方法級別注解,注解上面可以指定某些類上的哪些屬性需要忽略。然后對這些方法進行增強,增強邏輯中獲取到這些注解中的類以及這個類上面忽略的

下面是上面理論的一個簡單的實踐:

第一步:自定義注解:

package com.hotusm.jackson.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface IgnoreProperty {/*** 指定類*/Class<?> pojo();/***指定上面的類那些屬性需要過濾的 */String[] value();
}

上面這個注解就是我們后面要使用到的動態的在方法上面直接指定類需要忽略的屬性。

第二步:對ObjectMapper進行裝飾(寫的例子,不是很優雅)

package com.hotusm.jackson.annotation;import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.StringMemberValue;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;public class ObjectMapperBuilder {public ObjectMapper build(Method method) throws CannotCompileException{IgnoreProperty ignoreProperty = method.getAnnotation(IgnoreProperty.class);String[] value = ignoreProperty.value();Class<?> pojo = ignoreProperty.pojo();checkParamter(method,value,pojo);Class<?> clazz=doBuild(value);ObjectMapper objectMapper=new ObjectMapper();objectMapper.addMixInAnnotations(pojo, clazz);return objectMapper;}/*** 根據傳入的參數構造一個class* @throws CannotCompileException */public Class<?> doBuild(String[] values) throws CannotCompileException{ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeInterface("ProxyMixInAnnotation" + System.currentTimeMillis());ClassFile classFile = cc.getClassFile();ConstPool cp = classFile.getConstPool();AnnotationsAttribute attr = new AnnotationsAttribute(cp,AnnotationsAttribute.visibleTag);Annotation jsonIgnorePropertiesAnnotation = new Annotation(JsonIgnoreProperties.class.getName(), cp);BooleanMemberValue ignoreUnknownMemberValue = new BooleanMemberValue(false, cp);//
        ArrayMemberValue arrayMemberValue = new ArrayMemberValue(cp);Collection<MemberValue> memberValues = new HashSet<MemberValue>();for(int i=0;i<values.length;i++){StringMemberValue memberValue = new StringMemberValue(cp);// 將name值設入注解內
            memberValue.setValue(values[i]);memberValues.add(memberValue);}arrayMemberValue.setValue(memberValues.toArray(new MemberValue[]{}));jsonIgnorePropertiesAnnotation.addMemberValue("value", arrayMemberValue);jsonIgnorePropertiesAnnotation.addMemberValue("ignoreUnknown", ignoreUnknownMemberValue);attr.addAnnotation(jsonIgnorePropertiesAnnotation);classFile.addAttribute(attr); Class clazz = cc.toClass();return clazz;}protected void checkParamter(Object... objs){boolean isTrue=true;if(objs==null||objs.length<=0){isTrue=false;}for(Object obj:objs){if(obj==null){isTrue=false;}}if(!isTrue){throw new RuntimeException("參數出現錯誤");}}
}

上面這一步我們已經看到了熟悉的addMixInAnnotations。后面的參數就是我們使用javassist根據value數組創建的動態類,這個動態類增加了一個很重要的注解就是JsonIgnoreProperties(這個注解就是我們6中講的過濾屬性的),現在通過build方法返回的ObjectMapper已經滿足了動態的過濾屬性的。

下面是一個測試:

@Test@IgnoreProperty(pojo=Article.class,value={"user"})public void testJacksonAnnotation(){User user=new User();user.setName("hotusm");Article a1=new Article();a1.setTitle("t1");a1.setUser(user);Article a2=new Article();a2.setTitle("t2");a2.setUser(user);Article a3=new Article();a3.setTitle("t3");a3.setUser(user);List<Article> as=new ArrayList<Article>();as.add(a1);as.add(a2);as.add(a3);user.setArticles(as);ObjectMapper objectMapper;try {objectMapper = new ObjectMapperBuilder().build(Main.class.getMethod("testJacksonAnnotation"));String str = objectMapper.writeValueAsString(user);System.out.println(str);} catch (Exception e) {e.printStackTrace();}}

在打印出來的json數據我們就可以明顯的看出來已經把Article中的user屬性給過濾掉了。(注意,user和article是一對多的關系

總結:因為上面寫的一個例子只是為了顯示出問題,并沒有進行代碼的優化,以及功能的完善,如果是要在生產過程中使用的話,我們完全可以這樣做:1.注解可以在類或者是方法上面2.所有多出來的操作都應該是對客戶端程序員來說是透明的,我們可以通過方法的增強以及對ObjectMapper進行裝飾。3.將方法或者類上面的注解信息放入到緩存中去,而不用發每次都要提取一次

轉載于:https://www.cnblogs.com/zr520/p/5357459.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458883.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458883.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458883.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

太原理工大學c語言課程設計報告,[太原理工大學C語言實驗報告.doc

[太原理工大學C語言實驗報告本科實驗報告課程名稱&#xff1a; 程序設計技術B實驗項目&#xff1a;實驗地點&#xff1a; 明向校區軟件學院機房專業班級&#xff1a; 學號&#xff1a;學生姓名&#xff1a;指導教師&#xff1a; 呼克佑2014年 12月 日實驗名稱 實驗一 C語言的運…

網頁常用動態效果--懸浮廣告

關鍵在于動態獲取滾動坐標值 測試滾動事件 $(window).scroll(function(){ console.log($(window).scrolltop()); }) 獲取三個高度&#xff1a;窗口高度&#xff0c;盒子高度以及滾動坐標值&#xff0c;將廣告盒子設置為絕對定位&#xff0c;當鼠標滾動時&#xff0c;其top值為滾…

打印英文年歷C語言函數,C語言打印年歷

voidshow_year(int year){inti,j,k,t,n;                           // 用來輔助計數int table[24][21] {0};                     // 年歷數組int month_day[12] {31,28,31,30,31,30,31,31,30,31,30,31}; // 每月上限天數i…

ubuntu14.04配置caffe

ubuntu 14.04 64bit 安裝 請自行Google安裝&#xff0c;并修改源。 cuda 7.5 安裝 cuda7.5下載&#xff0c;選擇ubuntu 14.04的deb包下載安裝一些可能的依賴 sudo apt-get install linux-headers-$(uname -r) build-essential 安裝cuda sudo dpkg –i cuda-repo-<distro>…

iOS開發API常用英語名詞

iOS開發API常用英語名詞 0. indicating 決定 1.in order to 以便 2.rectangle bounds 矩形尺寸 3.applied 應用 4.entirety 全部 5.technique 方法 6.truncating 截短 7.wrapping 換行 8.string 字符串 9.familiar style 簡體 10.The styled text 主題樣式 11.Constants 常量 …

Win10 IIS本地部署網站運行時圖片和樣式不正常?

后期會在博客首發更新&#xff1a;http://dnt.dkill.net 異常處理匯總-服 務 器 http://www.cnblogs.com/dunitian/p/4522983.html 啟用關閉win功能&#xff0c;開啟一下靜態內容 收工~

設置IIS會話過期時間

打開默認網站----雙擊ASP--展開會話屬性---更改超時時間-轉載于:https://www.cnblogs.com/genesis/p/4816128.html

89c51單片機匯編語言,AT89C2051時鐘程序——MCS51單片機匯編程序

;; AT89C2051時鐘程序 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;定時器T0、T1溢出周期為50MS&#xff0c;T0為秒計數用&#xff0c; T1為調整時閃爍用&#xff0c;;P3.7為調整按鈕&#xff0c;P1口 為字符輸出口&#xff0c;采用共陽顯示管。;;;;;;;;;;;;;;;;;;;;;;;;;;;;…

c語言結構體指針初始化

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** 記得上周在飯桌上和同事討論C語言的崛起時&#xff0c;講到了內存管理方面 我說所有指…

WWDC 2013 Session筆記 - iOS7中彈簧式列表的制作

這是我的WWDC2013系列筆記中的一篇&#xff0c;完整的筆記列表請參看這篇總覽。本文僅作為個人記錄使用&#xff0c;也歡迎在許可協議范圍內轉載或使用&#xff0c;但是還煩請保留原文鏈接&#xff0c;謝謝您的理解合作。如果您覺得本站對您能有幫助&#xff0c;您可以使用RSS或…

H5學習之旅-H5列表(8)

列表的基本語法 ol&#xff1a;有序列表 ul&#xff1a;無序列表 li&#xff1a;列表項 dl&#xff1a;列表 dt&#xff1a;列表項 dd&#xff1a;列表描述 常用列表 1.無序列表&#xff1a;使用標簽 ul&#xff0c;li 屬性&#xff1a;disc&#xff08;默認實心圓&#xff09;…

c語言 文件夾是否純真,C語言面試題大匯總之華為面試題(轉)

1、局部變量能否和全局變量重名&#xff1f;答&#xff1a;能&#xff0c;局部會屏蔽全局。要用全局變量&#xff0c;需要使用 ":: "局部變量可以與全局變量同名&#xff0c;在函數內引用這個變量時&#xff0c;會用到同名的局部變量&#xff0c;而不會用到全局變量。…

virtual析構函數的作用?

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** 大家知道&#xff0c;析構函數是為了在對象不被使用之后釋放它的資源&#xff0c;虛函…

[codevs1039]數的劃分

這一題實際上是組合數學里面的經典問題&#xff0c;跟第二類Stirling數有些相似。可以把一個數值為n的數看成n個小球&#xff0c;劃分的份數k看作是k個盒子&#xff0c;那么本題的要求就是&#xff1a; 將n個小球放到k個盒子中&#xff0c;小球之間與盒子之間沒有區別&#xff…

c語言中用文件處理數據,C語言文件處理 -C語言從文件中讀寫格式化數據

從圖 1 所示的文件 fin.txt 中讀取學生姓名、身高和體重&#xff0c;計算并顯示它們的平均值&#xff0c;并且將顯示結果保存到文件 fout.txt 中。圖 1&#xff1a;輸入輸出文件示例C語言代碼清單 1&#xff1a;讀取學生姓名、身高和體重&#xff0c;計算并顯示它們的平均值#in…

MySQL于ON DUPLICATE KEY UPDATE采用

今天我們做的推斷插入用途MySQL于ON DUPLICATE KEY UPDATE。現在&#xff0c;Mark下面&#xff01;假設你想做的事&#xff0c;再有就是在數據庫中插入數據沒有數據、如果有數據更新數據&#xff0c;然后你可以選擇ON DUPLICATE KEY UPDATE。 ON DUPLICATE KEY UPDATE可以在UNI…

C++中Static作用和使用方法

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** 1、什么是static? static 是C中很常用的修飾符&#xff0c;它被用來控制變量的…

三星+android+7.0+自動糾正單詞,升級黨必看!三星S/Note系列更新Android 7.0指南

上周&#xff0c;雷科技(微信ID&#xff1a;leitech)曾為大家提供了一份Android 7.0升級預測名單。現在為了增加針對性&#xff0c;這次筆者就以三星最熱門的兩大旗艦S和Note系列為例&#xff0c;給大家提供一個全方面的更新指南。截至目前&#xff0c;三星S系列和Note系列的正…

iOS開發出錯whose view is not in the window hierarchy!的解決

大熊貓豬侯佩原創或翻譯作品.歡迎轉載,轉載請注明出處. 如果覺得寫的不好請多提意見,如果覺得不錯請多多支持點贊.謝謝! hopy ;) 一個簡單的單窗口App在運行時出現錯誤: 2016-04-07 14:28:48.411 BlurViewAndPopView[4364:168520] Warning: Attempt to present <UIAlertCon…

互聯網年鑒

最近互聯網行業&#xff0c;有一個詞說的比較多了&#xff0c;“資本寒冬”。作為&#xff0c;一個在這個行業里干的人&#xff0c;真心是好怕怕呢。 媽蛋&#xff0c;真怕哪天就突然失業了呀。所以&#xff0c;無聊的(操蛋的)去整理了一些98年開始一直到現在的互聯網中一些自己…