存在两个实体Permission
和Role
,它们之间的关系为@ManyToMany
;我想在他们的联接表中添加一些额外的字段,并按照本文enter link description here进行了编码,这是我的代码:
角色实体
@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);
}
}
权限实体
@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);
}
}
角色权限密钥
@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.addPermission(permission6);
permission6.addRole(role5);
roleRepository.save(role5);
permissionRepository.save(permission6);
然后,我在四个地方使用了相同的测试代码进行测试:
1。一个类实现ApplicationRunner;在Spring Boot启动之前初始化数据no exception
2。春季启动测试类@SpringBootTest; no exception
3。控制器类@Controller; exception thrown
4。服务类@Service; exception thrown
例外:
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]]
现在我很困惑问题是代码还是其他。我在1,2中使用测试代码在
一个角色和一个权限被成功地获得了预期的结果。但是代码在3,4中不起作用。
我需要一些提示和建议;顺便说一句,是否有任何值得学习的使用spring数据jpa的开源现实世界项目
,谢谢。
#################################################################
感谢@ Olivier Depriester的帮助。
我已经在调试模型中研究了RolePermission和Permission Entity中RolePermission Set的元素;
保持两个实体之间关系的集合类型为set
,我已经覆盖了equals()
和
可以确保具有相同ID的关系实体(RolePermission)不会出现在同一集合中的hashCode()
方法。
在save()
执行之前,“角色和权限集”中实际上只包含一个RolePermission对象。
在测试代码中,有两个.save()
操作,即使我注释了其中之一,也会抛出异常。
我想知道cascade
是否存在问题。
可以在某个地方成功执行测试代码很奇怪。
答案 0 :(得分:0)
呼叫role5.get().addPermission(permission6);
时,会为RolePermission
和role5
创建并保留(*)permission6
。
呼叫permission6.addRole(role5);
时,将为RolePermission
和role5
创建并保留另一个permission6
。
==>例外:会话中具有相同哈希码的同一实体的2个实例。
(*)将项目添加到持久对象的属性时,该项目将自动持久化。当您从存储库中获得role5
和permission6
时,它们就是持久对象
因此,您需要修改代码以创建1个单独的RolePermission
,可以将其分配给Role
和Permission