Spring Data JPA查询报错java.lang.StackOverflowError hibernate SpringBoot

发布时间 2023-08-24 17:41:18作者: 蔚然丶丶

toString()造成死循环,重写toString()方法

现象

测试JPA的多对多查询时,有一个User对象,该User有多个Role,然后报错

User

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

    @Id
    // 主键自动增长
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    Integer id;

    @Column(name = "username")
    String username;

    @Column(name = "password")
    String password;

    /**
     * 多对多关系会在创建用户和新角色时级联新增,关联表为user_role,当前对象在关联表对应的外键,和另一方在关联表中对应的外键
     */
    @ManyToMany(targetEntity = Role.class, cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
    @JoinTable(name = "user_role",
            joinColumns = {@JoinColumn(name = "u_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "r_id", referencedColumnName = "id")})
    List<Role> roles = new ArrayList<>();
}

Role

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

    @Id
    // 自增
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    Integer id;

    @Column(name = "role")
    String role;

    // 用户角色多对多
    @ManyToMany(mappedBy = "roles",fetch = FetchType.LAZY)
    List<User> users = new ArrayList<>();
}

Test

	// 存在“一/多对多"的情况时,需要添加事务,作为一个整体
	@Transactional(readOnly = true)
	@Test
	void select() {
		List<User> all = userRepository.findAll();
		System.out.println(all);
	}

Console

java.lang.StackOverflowError
	at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453)
	at com.example.springbootsecurityconcise.bean.Role.toString(Role.java:53)
	at java.base/java.lang.String.valueOf(String.java:4218)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:173)
	at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457)
	at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:590)
	at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453)
	at com.example.springbootsecurityconcise.bean.User.toString(User.java:62)
	at java.base/java.lang.String.valueOf(String.java:4218)
	at java.base/java.lang.StringBuilder.append(StringBuilder.java:173)
	at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457)
	// ...

原因

查看报错信息,StackOverflowError应该是哪里造成了死循环,多对多属性在创建时双方都有对方的List<Xxx>属性,在输出查看时使用了System.out.println(),会调用对象的toString()方法,在Xxx.toString()中输出Yyy时,又会调用Yyy.toString(),而Yyy.toString()中又有Xxx,就会死循环

解决

重写toString()方法,手动调用User.getXxx()方法,或减去另一方在toString()中的输出

    /**
     * 重写toString()方法,否则在sout输出时,会导致两个对象的toString()相互调用,现在需要去掉一方的关联字段输出
     * java.lang.StackOverflowError
     * @return
     */
    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", role='" + role + '\'' +
                // ", users=" + users +
                '}';
    }