spring-data-jpa一對多、多對多雙向關聯,查詢操作的時候進入死迴圈問題

csdndz發表於2019-09-27

此處以多對多為例,解決查詢時進入死迴圈問題

1.使用者實體類

@Entity
@Table(name = "t_sys_user")
@Data
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private String fullName;
    private String email;
    
    @ManyToMany(cascade = CascadeType.REMOVE)
    @JoinTable(name = "t_sys_user_roles")
    private List<Role> roles;
}

2.角色實體類

@Entity
@Table(name = "t_sys_role")
@Data
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @Lob
    private String remark;

    @ManyToMany(cascade = CascadeType.REMOVE, mappedBy = "roles")
    private List<User> users;   //被維護端

}

在使用插入資料的時候沒有任何問題,但是當查詢的時候報錯

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
	at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472) ~[tomcat-embed-core-9.0.22.jar:9.0.22]
	at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-9.0.22.jar:9.0.22]
	at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-9.0.22.jar:9.0.22]
	...
	
java.lang.StackOverflowError: null
	at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_131]
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_131]
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_131]
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) ~[na:1.8.0_131]
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73) ~[na:1.8.0_131]
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368) ~[na:1.8.0_131]
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362) ~[na:1.8.0_131]
	at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_131]
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361) ~[na:1.8.0_131]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_131]
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) ~[na:1.8.0_131]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_131]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:737) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) ~[jackson-databind-2.9.9.jar:2.9.9]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) ~[jackson-databind-2.9.9.jar:2.9.9]
	...

返回的資料

[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","users":
...

首先這是雙向關聯,雙向關聯中你如果從資料庫裡面查詢一個User物件,那麼User物件裡面有Role,Role裡面又有User物件,那麼你用syso輸出User物件,如果toString方法裡面包含有輸出User.roles的話,那麼是必然會造成死迴圈的。如果你用sping mvc等框架將後臺資料返回給前臺也是同理,也會造成返回的JSON資料死迴圈

那麼要如何解決?

解決辦法就是在序列化例項的時候中斷迴圈就好。首先你要理解這不是spring-data-jpa的問題,這是一個序列化的問題

例如如果是用jsckson對資料進行序列化的的話,可以使用下面的註解。

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 
@Entity
@Table(name = "t_sys_user")
@Data
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private String fullName;
    private String email;
    
    @JsonIgnoreProperties("users")
    @ManyToMany(cascade = CascadeType.REMOVE)
    @JoinTable(name = "t_sys_user_roles")
    private List<Role> roles;
}

序列化使用者類角色欄位裡的使用者集合欄位user.roles[i].users時就忽略該欄位
Role類修改如下:


	@JsonIgnoreProperties(value = { "roles" })
    @ManyToMany(cascade = CascadeType.REMOVE, mappedBy = "roles")
    private List<User> users;   //被維護端

返回的結果無user.roles[i].users欄位

[{"id":1,"username":"admin","password":"$2a$10$.UblZbe8b/ESRiXrajVgo.HuhbJUezsPgpyD.tVrJraFmegiup.aS","fullName":"管理員","email":"admin@qq.com","roles":[{"id":1,"name":"管理員","remark":"系統管理員","permissions":[]}],"enabled":true,"version":0,"enabledStr":"有效","authorities":null,"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true}]

問題解決!

相關文章