我用spring data jpa 写了两个多对多关系的实体Role和Permission,想在关系表里添加一些额外的字段,参考了这篇文章:JAP多对多最佳实践
一下时代码:
Role 实体类
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@GenericGenerator(name = "jpa-uuid", strategy = "uuid")
public class Role {
@Id
@GeneratedValue(generator = "jpa-uuid")
private String id;
@JsonIgnore @ToString.Exclude
@OneToMany(mappedBy = "role",cascade = CascadeType.ALL,orphanRemoval = false,fetch = FetchType.EAGER)
private Set<RolePermission> role_permission;
public void addPermission(Permission permission){
RolePermission rolePermission = new RolePermission(this,permission);
role_permission.add(rolePermission);
permission.getRole_permission().add(rolePermission);
}
public void removePermission(Permission permission){
for(Iterator<RolePermission> iterator = role_permission.iterator();iterator.hasNext();){
RolePermission rolePermission = iterator.next();
if(rolePermission.getRole().equals(this) && rolePermission.getPermission().equals(permission)){
iterator.remove();
rolePermission.getRole().role_permission.remove(rolePermission);
rolePermission.setRole(null);
rolePermission.setPermission(null);
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Role role = (Role) o;
return Objects.equals(id, role.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Permission 实体类
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@GenericGenerator(name = "jpa-uuid", strategy = "uuid")
public class Permission {
@Id
@GeneratedValue(generator = "jpa-uuid")
private String id;
@JsonIgnore @ToString.Exclude
@OneToMany(mappedBy = "permission",cascade = CascadeType.ALL,orphanRemoval = true,fetch = FetchType.EAGER)
private Set<RolePermission> role_permission;
public void addRole(Role role){
RolePermission rolePermission = new RolePermission(role,this);
role_permission.add(rolePermission);
role.getRole_permission().add(rolePermission);
}
public void removeRole(Role role){
for(Iterator<RolePermission> iterator = role_permission.iterator(); iterator.hasNext();){
RolePermission rolePermission = iterator.next();
if(rolePermission.getRole().equals(role) && rolePermission.getPermission().equals(this)){
iterator.remove();
rolePermission.getRole().getRole_permission().remove(rolePermission);
rolePermission.setRole(null);
rolePermission.setPermission(null);
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Permission that = (Permission) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Role Permission 主键类
@Embeddable
@Setter
@Getter
@NoArgsConstructor
public class RolePermissionKey implements Serializable {
private static final long serialVersionUID = 4686642987484483168L;
@Column(name = "role_id")
private String role_id;
@Column(name = "permission_id")
private String permission_id;
public RolePermissionKey(String roleId, String permissionId) {
this.role_id=roleId;
this.permission_id=permissionId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RolePermissionKey that = (RolePermissionKey) o;
return role_id.equals(that.role_id) &&
permission_id.equals(that.permission_id);
}
@Override
public int hashCode() {
return Objects.hash(role_id, permission_id);
}
}
带着额外字段的实体表
@Entity
@Setter
@Getter
@NoArgsConstructor
public class RolePermission implements Serializable {
private static final long serialVersionUID = 8274025418699729303L;
@EmbeddedId
RolePermissionKey id;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("role_id")
Role role;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("permission_id")
Permission permission;
Date create_date =new Date();
public RolePermission(Role role,Permission permission) {
this.role=role;
this.permission=permission;
this.id = new RolePermissionKey(role.getId(),permission.getId());
this.create_date=new Date();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RolePermission that = (RolePermission) o;
return Objects.equals(role, that.role) &&
Objects.equals(permission, that.permission);
}
@Override
public int hashCode() {
return Objects.hash(role, permission);
}
}
测试代码
Role role5 = roleRepository.findByTitle("Role5");
Permission permission6 = permissionRepository.findByTitle("Permission6");
role5.get().addPermission(permission6);
permission6.addRole(role5);
roleRepository.save(role5);
permissionRepository.save(permission6);
现在的情况是我将测试用的代码片段贴在了4各不同的地方,有的地方能执行成功得到预期结果,有的地方会抛出异常,具体如下:
1. ApplicationRunner 的实现类;boot启动时初始化数据,正常执行
2. 一个测试类 @SpringBootTest;正常执行
3. 一个 @Controller 类 ;抛异常
4. 一个 @Service 类;抛异常
异常信息:2020-05-05 09:11:35.952 WARN 17000 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [com.xxx.model.authentication.RolePermission#com.xxx.model.authentication.RolePermissionKey@9e2c8771]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.xxx.model.authentication.RolePermission#com.xxx.model.authentication.RolePermissionKey@9e2c8771]]
不清楚为什么会有的地方执行成功,有的地方会抛出异常。
两个实体类中保存关系的集合用的时set,并且重写了两个实体的equals()和hashcode()方法,关系集合中不会出现重复ID的对象。
测试代码中有两个save()的动作,注释掉其中任何一个依然会报异常。麻烦大家 帮忙看看,谢谢了。
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…