JPA 懒加载的一个坑 大神可绕路
package com.liuzm.controller;
import com.liuzm.entity.UacRole;
import com.liuzm.entity.UacUser;
import com.liuzm.service.UacUserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Set;
@RestController
public class UserController {
@Resource
private UacUserService uacUserService;
@GetMapping("/")
public Set<UacRole> findByLoginName() {
UacUser uacUser = this.uacUserService.findByLoginName("admin");
System.out.println(uacUser);
Set<UacRole> roles = uacUser.getRoles();
System.out.println(roles.size());
return roles;
}
}
package com.liuzm.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "tbl_uac_role")
public class UacRole implements Serializable {
/**
* serialVersionUID:用一句话描述这个变量表示什么.
* @since JDK 1.7
*/
private static final long serialVersionUID = -6263200210499558891L;
/**
* ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 角色编码
*/
@Column(name = "role_code")
private String roleCode;
@JsonIgnore
@ManyToMany
@JoinTable(name = "tbl_uac_role_user", joinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")})
private Set<UacUser> users = new HashSet<>();
public UacRole() {
}
public UacRole(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleCode() {
return roleCode;
}
public void setRoleCode(String roleCode) {
this.roleCode = roleCode;
}
public Set<UacUser> getUsers() {
return users;
}
public void setUsers(Set<UacUser> users) {
this.users = users;
}
@Override
public String toString() {
return "UacRole{" +
"id=" + id +
", roleCode='" + roleCode + '\'' +
", users=" + users +
'}';
}
}
package com.liuzm.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "tbl_uac_user")
public class UacUser implements Serializable {
/**
* serialVersionUID:用一句话描述这个变量表示什么.
* @since JDK 1.7
*/
private static final long serialVersionUID = 3439493474130693948L;
/**
* ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 登录名
*/
@Column(name = "login_name")
private String loginName;
@ManyToMany(mappedBy = "users")
@JsonIgnore
private Set<UacRole> roles = new HashSet<>();
@Override
public String toString() {
return "UacUser{" +
"id='" + id + '\'' +
", loginName='" + loginName + '\'' +
", roles=" + roles +
'}';
}
public UacUser() {
}
public UacUser(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public Set<UacRole> getRoles() {
return roles;
}
public void setRoles(Set<UacRole> roles) {
this.roles = roles;
}
}
package com.liuzm.repository;
import com.liuzm.entity.UacUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UacUserRepository extends JpaRepository<UacUser, Long> {
UacUser findByLoginName(String loginName);
}
package com.liuzm.service;
import com.liuzm.entity.UacUser;
public interface UacUserService {
UacUser findByLoginName(String loginName);
}
server:
port: 8000
tomcat:
uri-encoding: UTF-8
spring:
application:
name: jpa-test
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
错误如下
Hibernate: select uacuser0_.id as id1_2_, uacuser0_.login_name as login_na2_2_ from tbl_uac_user uacuser0_ where uacuser0_.login_name=?
2017-04-19 13:49:46.187 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [admin]
2017-04-19 13:49:46.191 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_2_] : [BIGINT]) - [222]
2017-04-19 13:49:46.195 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([login_na2_2_] : [VARCHAR]) - [admin]
Hibernate: select roles0_.user_id as user_id2_1_0_, roles0_.role_id as role_id1_1_0_, uacrole1_.id as id1_0_1_, uacrole1_.role_code as role_cod2_0_1_ from tbl_uac_role_user roles0_ inner join tbl_uac_role uacrole1_ on roles0_.role_id=uacrole1_.id where roles0_.user_id=?
2017-04-19 13:49:46.208 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [222]
2017-04-19 13:49:46.213 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_1_] : [BIGINT]) - [1234]
2017-04-19 13:49:46.213 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([role_cod2_0_1_] : [VARCHAR]) - [admin]
2017-04-19 13:49:46.214 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([user_id2_1_0_] : [BIGINT]) - [222]
2017-04-19 13:49:46.214 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([role_id1_1_0_] : [BIGINT]) - [1234]
Hibernate: select users0_.role_id as role_id1_1_0_, users0_.user_id as user_id2_1_0_, uacuser1_.id as id1_2_1_, uacuser1_.login_name as login_na2_2_1_ from tbl_uac_role_user users0_ inner join tbl_uac_user uacuser1_ on users0_.user_id=uacuser1_.id where users0_.role_id=?
2017-04-19 13:49:46.217 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1234]
2017-04-19 13:49:46.218 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_2_1_] : [BIGINT]) - [222]
2017-04-19 13:49:46.218 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([role_id1_1_0_] : [BIGINT]) - [1234]
2017-04-19 13:49:46.218 TRACE 14504 --- [nio-8000-exec-1] o.h.type.descriptor.sql.BasicExtractor : extracted value ([user_id2_1_0_] : [BIGINT]) - [222]
2017-04-19 13:49:46.271 ERROR 14504 --- [nio-8000-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
at java.util.AbstractCollection.toString(AbstractCollection.java:454) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacUser.toString(UacUser.java:37) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacRole.toString(UacRole.java:69) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacUser.toString(UacUser.java:37) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacRole.toString(UacRole.java:69) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacUser.toString(UacUser.java:37) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacRole.toString(UacRole.java:69) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacUser.toString(UacUser.java:37) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at com.liuzm.entity.UacRole.toString(UacRole.java:69) ~[classes/:na]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
at java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_60]
at java.util.AbstractCollection.toString(AbstractCollection.java:462) ~[na:1.8.0_60]
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:300) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_60]
在网上查了一下,和大神们的问题略有不同,这里具体原因是 两个类都有toString(), 而两个类中同时使用了懒加载(默认)这样在A类toString的时候会发sql查询B类,而B类toString()又会发sql查A类这样会发生死循环最后导致java.lang.StackOverflowError.初用JPA 出现问题引以为鉴.
完整代码
最后做一下说明 如果是springmvc jackson报response错误 那么最好是 多对多维护关系都不要toString() Set集合对象, 然后使用 dto 或者 vo 去做数据的传输去解决这个问题. 具体原因有待日后解决