情况
我有一个 RoleEntity
和一个 UserEntity
。 RoleEntity
包含 UserEntity
的列表,反之亦然,从而产生多对多
关系。
我的 UserEntity
:
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(unique = true, nullable = false)
private String username;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
@Column(nullable = false)
private String email;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.REFRESH)
@JoinTable(name = "uas_user_role",
joinColumns = @JoinColumn(name = "uas_user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "uas_role_id", referencedColumnName = "id"),
uniqueConstraints = {
@UniqueConstraint(columnNames = {"uas_user_id", "uas_role_id"})}
)
private Set<RoleEntity> roles = new HashSet<>();
...
}
我的 RoleEntity
:
public class RoleEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(unique = true, nullable = false)
private String name;
@ManyToMany(mappedBy = "roles")
private Set<UserEntity> users;
@ManyToMany
@JoinTable(
name = "uas_role_permission",
joinColumns
= @JoinColumn(name = "uas_role_id", referencedColumnName = "id"),
inverseJoinColumns
= @JoinColumn(name = "uas_permission_id", referencedColumnName = "id"),
uniqueConstraints = {
@UniqueConstraint(columnNames = {"uas_role_id", "uas_permission_id"})}
)
private Set<PermissionEntity> permissions;
...
}
目标
现在我想通过简单地更新单个实体来更改映射。例如,我想将 UserEntity
添加到 id=1 的 RoleEntity
:
{
"id": 1,
"name": "Admin_Role",
"users": [{"id": 1}
]
}
将此 RoleEntity
保存到它的角色存储库 (JPARepository.saveAndFlush(Serializable)
),成功解析隐含的 UserEntity
并返回更新的角色实体
{
"id": 1,
"name": "Admin_Role",
"users": [{
"id": 1,
"username": "admin",
"firstName": "Administrator",
"lastName": "Administrator",
"email": "info@example.com",
}
]
}
问题
我的测试都通过了,因为我只检查了保存命令的返回值。
现在我发现在坚持之后执行 roleRepository.findOne(1)
时,我将收到以下 RoleEntity
:
{
"id": 1,
"name": "Admin_Role",
"users": []
}
UserEntity
映射没有持久化。
此外,在设置 spring.jpa.show-sql=true
时,我看到在 saveAndFlush
命令期间仅执行了 SELECT
操作。
我只想使用 创建映射,不想允许编辑映射实体,这就是我禁用任何级联的原因。
我希望通过这种方法创建一个映射。我究竟做错了什么?
解决方案
正如 Amer Qarabsa 提到的: 要创建双向映射,需要在两侧进行设置。在保存实体后添加一行就可以了:
@Transactional
public RoleDto updateRole(RoleDto role) {
RoleEntity roleEntity = roleRepository.findOne(role.getId());
if (roleEntity==null){
throw new ResourceNotFoundException();
}
final RoleEntity savedRoleEntity = roleRepository.saveAndFlush(roleEntity);
savedRoleEntity.getUsers().forEach(user -> user.getRoles().add(savedRoleEntity));
return savedRoleEntity;
}
使用 .saveAndFlush(roleEntity)
我将首先更新 RoleEntity
并让 JPA 解析 UserEntity
。在此之后,我只需将 RoleEntity
设置为角色的每个 UserEntity
。在我的例子中,我不需要手动保存,因为事务将负责保存任何未保存的更改。
最佳答案
在双向关系中,做到这一点并不简单,您不能如此简单地获取 json 并将其映射到实体,关系的每一端都应该有对另一端的引用,因此在您的情况下,您需要采用角色实体中的每个用户实体并设置它的角色实体然后保存。
关于java - Spring + JPA/Hibernate 不保存多对多映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47903876/