Spring数据JPA-休眠多对多关系在链接实体表中插入null
Posted
技术标签:
【中文标题】Spring数据JPA-休眠多对多关系在链接实体表中插入null【英文标题】:Spring data JPA- Hibernate Many to Many relation is inserting null in link entity table 【发布时间】:2021-01-05 14:32:49 【问题描述】:我在用户和角色之间有多对多的关系
用户实体:
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Entity
@Data
@EqualsAndHashCode(callSuper = false, exclude = "roles")
@ToString( exclude = "roles")
@NoArgsConstructor
public class User
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String lanId;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Set<UserRole> roles = new HashSet<>();
public User(String lanId)
this.lanId = lanId;
// Utility Method to sync on both sides
public void addRole(Role role, boolean isPrivileged)
UserRole userRole = new UserRole();
userRole.setUser(this);
userRole.setRole(role);
userRole.setPrivileged(isPrivileged);
roles.add(userRole);
role.getUsers().add(userRole);
角色实体:
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Entity
@Data
@EqualsAndHashCode(callSuper = false, exclude = "users")
@ToString( exclude = "users")
@NoArgsConstructor
public class Role
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String roleName;
@OneToMany(mappedBy = "role" ,cascade = CascadeType.ALL)
private Set<UserRole> users = new HashSet<>();
public Role(String roleName)
this.roleName =roleName;
用户角色实体:
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Data
@EqualsAndHashCode(callSuper = false ,exclude = "privileged")
@NoArgsConstructor
public class UserRole implements Serializable
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@ManyToOne
private User user;
@Id
@ManyToOne
private Role role;
private boolean privileged;
服务类:
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.sample.m2m.dto.RolesDto;
import com.sample.m2m.repository.Role;
import com.sample.m2m.repository.RoleRepository;
import com.sample.m2m.repository.User;
import com.sample.m2m.repository.UserRepository;
@Service
public class SampleService
@Autowired
private UserRepository userRepo;
@Autowired
private RoleRepository roleRepo;
public void addEntity(String lanId,List<RolesDto> roles)
// adding roles to DB first
addNewRoles(lanId,roles);
addUserRole(lanId,roles);
@Transactional
public void addNewRoles(String lanId,List<RolesDto> roles)
//Set<String> roles = Set.of("admin", "read","write");
// Set<String> roles = Set.of("opr");
Set<Role> roleSet = new HashSet<Role>();
for(RolesDto role :roles)
Role roleDB = roleRepo.findByRoleName(role.getRoleName());
if(roleDB ==null)
roleDB = new Role(role.getRoleName());
roleSet.add(roleDB);
roleRepo.saveAll(roleSet);
@Transactional
public void addUserRole(String lanId,List<RolesDto> roles)
//Set<String> roles = Set.of("admin", "read","write");
//Set<String> roles = Set.of("opr");
User userDB = userRepo.findByLanId(lanId);
if(userDB == null)
userDB = new User(lanId);
for(RolesDto role : roles)
Role roledb = roleRepo.findByRoleName(role.getRoleName());
userDB.addRole(roledb, true);
else
for(RolesDto role : roles)
Role roledb = roleRepo.findByRoleName(role.getRoleName());
userDB.addRole(roledb, true);
userRepo.save(userDB);
示例输入:1:第一次保存 - 成功
"lanId":"ABC123",
"roles" :[
"roleName" : "opr"
]
示例输入 2:将其他角色保存给同一用户 - 失败
"lanId":"AB123",
"roles" :[
"roleName" : "admin"
,
"roleName" : "read"
,
"roleName" : "write"
]
异常:(试图将 null 插入到链接实体中的用户和角色中)
2020-09-18 11:12:34.379 DEBUG 24862 --- [nio-8080-exec-5] org.hibernate.SQL :
select
userrole0_.user_id as user_id2_2_0_,
userrole0_.role_id as role_id3_2_0_,
userrole0_.privileged as privileg1_2_0_
from
pam.user_role userrole0_
where
userrole0_.user_id=?
and userrole0_.role_id=?
2020-09-18 11:12:34.379 TRACE 24862 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1739260]
2020-09-18 11:12:34.379 TRACE 24862 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1739261]
2020-09-18 11:12:34.393 ERROR 24862 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [com.sample.m2m.repository.UserRole#UserRole(user=null, role=null, privileged=true)]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.sample.m2m.repository.UserRole#UserRole(user=null, role=null, privileged=true)]] with root cause
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.sample.m2m.repository.UserRole#UserRole(user=null, role=null, privileged=true)]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
我是否在映射或用户实体的实用程序方法中遗漏了什么。非常感谢任何帮助。
【问题讨论】:
【参考方案1】:UserRole 属性不是表的主 ID,所以 @Id 不应该在这两个上。
您应该向 UserRole 添加一个 ID 属性并使用 @Id 和 @GeneratedValue 对其进行注释。
@ManyToOne 将在数据库中产生一个外键
【讨论】:
通过将 ID 属性添加到 UserRole 我能够成功保存,但只是好奇根据 Hibernate docs for Many to Many Bi Directional life cycle entity ,文档没有提到有一个单独的 ID 属性,但将 ID 添加到实体 .docs.jboss.org/hibernate/orm/5.2/userguide/html_single/….【参考方案2】:也尝试保存角色,在用户实体的addrole函数中更改角色后,您还没有存储角色。
【讨论】:
在我使用 Utility 方法设置用户和角色之间的链接之前,角色已经保存。以上是关于Spring数据JPA-休眠多对多关系在链接实体表中插入null的主要内容,如果未能解决你的问题,请参考以下文章
Spring,JPA:如何使用多对多关系桥表设置查询另一个实体下的实体