权限模块的开发中,采用了RBAC设计模式,数据表间存在这样的关系:用户和角色多对多,角色和权限多对多。 在JPA中使用了@ManyToMany的注解来自动绑定多对多的表关系,自动操作关联表。
这是用户和角色实体类中配置的多对多关系【问题出现来源于此】
用户实体类代码片段: 角色实体类代码片段: 业务逻辑出现问题的代码片段: 问题就出现在:
Set<SysRole> roles = new HashSet<>(); for (String roleId : roleIdList) { SysRole role = sysRoleDao.findById(roleId).get(); roles.add(role); }在实体类中,我只考虑乐在Json转换的时候,不去转换多对多映射的属性。同时,对于多对多的属性我这里使用了Set<>集合来存储,考虑的是避免重复。在业务逻辑中,我又再次使用了Set<>集合来临时存储查询出来的角色信息。
在javaSE核心基础学习时,我了解到HashSet内部保证存储的角色实体唯一性的原理是,角色实体类重新实现了hashCode和equals方法。
出现堆栈溢出的原因就来源于此,在hashCode和equals方法中如果没有特别说明,会对多对多属性也进行处理。而且用户实体类和角色实体类都有互相包含,并且都有实现hashCode和equals方法,这就出现了循环的情况。
在执行roles.add(role);语句的时候,会执行SysRole的hashCode和equals方法,这时根据懒加载机制,就会对users集合中元素进行查询,同理,SysUser也会执行hashCode和equals方法,这时又开始对roles集合中元素进行查询,如此就出现了一个死循环的情况。
实体类对于toString、hashCode和equals方法都有相应的注解,其中可以设置来排除一些属性的转换。 故而,添加注解如下:
角色实体类:
@ToString(exclude = {"users"}) @EqualsAndHashCode(exclude= {"users"})因为用户和角色多对多关系里,角色实体被用户实体来维护,所以我们一般在角色实体中不操作用户实体,而在用户实体中可以操作角色实体。故而,我在角色实体类中的toString、hashCode和equals方法都把用户属性排除,对于用户实体,在toString、hashCode和equals方法中都还是处理角色对象。
这里,也是符合常理的做法,这就体现了用户要相同就要保证他的角色也要相同,而对于角色要相同,不用考虑与它关联的用户是否相同。
【使用了lombok插件、使用了swagger插件、JPA注解】
用户实体类 @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<>(); } 角色实体类 @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<>(); }