Solve One-to-multiple loop references in JPA, and solve by using JPA loop references
The solution is actually not a perfect solution. I just wrote it to record the problem or to see if any of my friends would know.
The following describes the problem:
This is the problem. When I query a one-to-many entity, the tool will pop up directly. I almost guess where the endless loop is, and finally waited for a long time to see why, it was a heap overflow, followed by a jsckson error. So it must be a serialization problem.
This is jackson's error:
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)
This is a loop reference error:
Severe: Servlet. service () for servlet [springDispatcherServlet] in context with path [/Shop] threw exception [Request processing failed; nested exception is org. springframework. http. converter. httpMessageNotWritableException: cocould not write JSON: Infinite recursion (StackOverflowError) (through reference chain: com. web. module. index. model. entity. account ["user"]-> com. web. module. index. model. entity. user [" Ccounts "]-> 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 ............................... There are many identical errors
The following are two entities:
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. onetovel; 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") @ Entitypublic class User implements Serializable {/***/private static final long serialVersionUID = 1L; @ XmlElement @ Id private String id; /*** validate applies to springmvc */@ XmlElement // @ NotEmpty private String name; @ JsonIgnore @ onetovel (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;@Entitypublic 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 + "]"; } }
Later, I went to the Internet and looked at it. Many people encountered this problem. There are also many solutions.
1. set @ JsonIgnore on the associated object. This annotation indicates that this attribute is ignored during serialization. however, my current logic is that attributes in the associated object must be used in the page, so this cannot be done. Otherwise, this data cannot be obtained on the page.
Uncaught TypeError: Cannot read property 'name' of undefined (1, 2)
2. the one-way, many-to-one method is adopted, so that there will be no loop problem. This is indeed a solution, but if you need to use multiple methods on the one side, it's hard. So I still don't feel very satisfied.
3. Later I thought about it. If so, why don't I use @ JsonIgnore on the other side. It is not used on the page currently. In fact, this is the second one, which is similar. It is a little different that it can still be used in other services except that the data in the page cannot be displayed. This is also a solution that I mentioned earlier that is not satisfactory.
4. the fourth solution is similar to the previous 3. When we use multiple sides, it can be correctly displayed, but when we use the one end, we can use the List to assemble it ourselves, a bit like the following code:
@ 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", "Coal" + I); map. put ("units", "pause"); map. put ("consumer", "XX Logistics" + id); map. put ("unitPrice", dfmt. format (price); map. put ("number", dfmt. format (number); map. put ("count", dfmt. format (price * number); list. add (map) ;}// set the date format return list ;}
In this way, jackson will not encounter errors during serialization, and it will not be used like A. B. name, and it will be easier to use. We can use the following in 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>
This solves this problem perfectly.