JPA查询堆栈溢出BUG的解决

1、问题出现的背景

权限模块的开发中,采用了RBAC设计模式,数据表间存在这样的关系:用户和角色多对多,角色和权限多对多。
在JPA中使用了@ManyToMany的注解来自动绑定多对多的表关系,自动操作关联表。

这是用户和角色实体类中配置的多对多关系【问题出现来源于此

用户实体类代码片段:
在这里插入图片描述
角色实体类代码片段:
在这里插入图片描述
业务逻辑出现问题的代码片段:
在这里插入图片描述
问题就出现在:

 	Set<SysRole> roles = new HashSet<>();
    for (String roleId : roleIdList) {
    
    
    	SysRole role = sysRoleDao.findById(roleId).get();
    	roles.add(role);
   	}

2、分析问题出现的原因

在实体类中,我只考虑乐在Json转换的时候,不去转换多对多映射的属性。同时,对于多对多的属性我这里使用了Set<>集合来存储,考虑的是避免重复。在业务逻辑中,我又再次使用了Set<>集合来临时存储查询出来的角色信息。

在javaSE核心基础学习时,我了解到HashSet内部保证存储的角色实体唯一性的原理是,角色实体类重新实现了hashCodeequals方法。

出现堆栈溢出的原因就来源于此,在hashCodeequals方法中如果没有特别说明,会对多对多属性也进行处理。而且用户实体类和角色实体类都有互相包含,并且都有实现hashCodeequals方法,这就出现了循环的情况。

在执行roles.add(role);语句的时候,会执行SysRolehashCodeequals方法,这时根据懒加载机制,就会对users集合中元素进行查询,同理,SysUser也会执行hashCodeequals方法,这时又开始对roles集合中元素进行查询,如此就出现了一个死循环的情况。

3、问题的解决

实体类对于toStringhashCodeequals方法都有相应的注解,其中可以设置来排除一些属性的转换。
故而,添加注解如下:

角色实体类:

@ToString(exclude = {
    
    "users"})
@EqualsAndHashCode(exclude= {
    
    "users"})

因为用户和角色多对多关系里,角色实体被用户实体来维护,所以我们一般在角色实体中不操作用户实体,而在用户实体中可以操作角色实体。故而,我在角色实体类中的toStringhashCodeequals方法都把用户属性排除,对于用户实体,在toStringhashCodeequals方法中都还是处理角色对象。

这里,也是符合常理的做法,这就体现了用户要相同就要保证他的角色也要相同,而对于角色要相同,不用考虑与它关联的用户是否相同。

代码实现

【使用了lombok插件、使用了swagger插件、JPA注解】

  1. 用户实体类
@Data
@Entity
@Table(name = "sys_user")
public class SysUser implements Serializable {
    
    
    @ApiModelProperty(value = "用户id")
    @Id
    private String id;
    @ApiModelProperty(value = "手机")
    private String mobile;
    @ApiModelProperty(value = "用户名")
    private String username;
    @ApiModelProperty(value = "密码")
    private String password;
    @ApiModelProperty(value = "可用状态")
    private Integer enableState;
    @ApiModelProperty(value = "创建时间")
    private Date gmtCreate;
    @ApiModelProperty(value = "修改时间")
    private Date gmtUpdate;
    @ApiModelProperty(value = "小组id")
    private String groupId;
    @ApiModelProperty(value = "用户等级")
    private String level;
    @ApiModelProperty(value = "用户头像")
    private String staffPhoto;
    @ManyToMany
    @JsonIgnore
    @JoinTable(name="sys_user_role",
    joinColumns = {
    
    @JoinColumn(name = "user_id",referencedColumnName = "id")},
    inverseJoinColumns = {
    
    @JoinColumn(name = "role_id",referencedColumnName = "id")})
    private Set<SysRole> roles = new HashSet<>();
}
  1. 角色实体类
@Data
@ToString(exclude = {
    
    "users"})
@EqualsAndHashCode(exclude= {
    
    "users"})
@Entity
@Table(name = "sys_role")
public class SysRole implements Serializable {
    
    
    @ApiModelProperty(value = "角色id")
    @Id
    private String id;
    @ApiModelProperty(value = "角色名称",required = true)
    private String name;
    @ApiModelProperty(value = "描述")
    private String description;
    @ApiModelProperty(value = "小组id")
    private String groupId;
    @ManyToMany(mappedBy = "roles")
    @JsonIgnore
    private Set<SysUser> users = new HashSet<>();
    @ManyToMany
    @JsonIgnore
    @JoinTable(name = "sys_role_permission",
    joinColumns = {
    
    @JoinColumn(name="role_id",referencedColumnName = "id")},
    inverseJoinColumns = {
    
    @JoinColumn(name = "permission_id",referencedColumnName = "id")})
    private Set<SysPermission> perms = new HashSet<>();
}

猜你喜欢

转载自blog.csdn.net/weixin_38708854/article/details/107080007
今日推荐