如何持久化@ManyToMany关系 - 重复条目或分离实体

Aur*_*e77 15 java many-to-many jpa duplicates unique-constraint

我想用ManyToMany关系来保持我的实体.但是在持久化过程中我遇到了一些问题.

我的实体:

@Entity
@Table(name = "USER")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long userId;

    @Column(name = "NAME", unique = true, nullable = false)
    String userName;

    @Column(name = "FORNAME")
    String userForname;

    @Column(name = "EMAIL")
    String userEmail;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "USER_USER_ROLES", joinColumns = @JoinColumn(name = "ID_USER"), inverseJoinColumns = @JoinColumn(name = "ID_ROLE"))
    List<UserRoles> userRoles = new ArrayList<UserRoles>();

    // getter et setter
}
Run Code Online (Sandbox Code Playgroud)

@Entity
@Table(name = "USER_ROLES")
public class UserRoles implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long userRolesId;

    @Column(unique = true, nullable = false, name = "ROLE_NAME")
    String roleName; 

    // getter et setter
}
Run Code Online (Sandbox Code Playgroud)

服务代码:

User user = new User();
UserRoles role;
try {
    role = userRolesServices.getUserRoleByName("ROLE_USER"); // find jpql - transaction
} catch (RuntimeException e) {
    LOGGER.debug("No Roles found");
    role = new UserRoles("ROLE_USER"); // create new
}
user.addUserRole(role);
user.setUserName(urlId);
user.setUserForname(fullName);
user.setUserEmail(email);
userServices.createUser(user); // em.persist(user) - transaction
Run Code Online (Sandbox Code Playgroud)

第一次,当我尝试用UserRoles"ROLE_USER"来保持用户时,没问题.将插入User和UserRoles以及联接表.

我的问题是当我尝试使用相同的UserRoles持久保存第二个用户时.我通过查找UserRolesServices.getUserRoleByName (...)来检查UserRoles是否存在.如果存在 - >将此UserRoles添加到用户列表(id +角色名称),否则我创建一个新的(仅角色名称).

当我尝试持久保存第二个用户时,我获得以下异常: "要保留的分离实体:...... UserRoles"(可能因为getUserRoleByName在另一个事务中执行)

如果我不使用getUserRoleByName(仅*new UserRoles("ROLE_USER");*),我将获得以下异常: "... ConstraintViolation:'ROLE_NAME'的重复条目......"

那么,如何使用@ManyToMany关系正确地持久化实体呢?

Ami*_*nde 24

对于上述问题,我会说你的实体关系级联是错误的.请考虑这一点:用户可以拥有多个角色,但系统中可以存在固定数量的角色.所以来自User实体的CASCADE ALL 没有任何意义,因为生命周期不UserRoles应该依赖于User实体生命周期.例如,当我们删除时User,UserRoles不应该删除.

只有在传递已将主键设置为持久的对象时,才会发生分离实体持久异常.

删除级联,您的问题现在将解决,您只需要决定如何插入用户角色.据我所知,应该有单独的功能.

也不要使用ArrayList,使用HashSet.ArrayList允许重复.

  • @Aure77,我和你有同样的问题。我可以将级联设置为 MERGE,但是当 UserRoles 是新的时,将不再创建它们...并且当 hibernate 尝试插入引用用户角色表上不存在的行的连接表时,我会遇到约束冲突。那么,我错过了什么吗?谢谢! (2认同)

Ata*_*ais 8

如果有人向我和作者提出相同类型的问题,我会提供我的答案.

基本上我面临的是一种情况,当我有一个表是某种CONSTANT值.而另一个会改变,但它应该map(many to many)到那些CONSTANTS.

确切的问题是USERS,它是ROLES.

图

Roles将被知道并在系统启动时添加,因此它们永远不会被删除.即使没有用户会有一些Role仍然应该在系统中.

使用JPA的类实现:

用户:

@Entity
@Table(name = "USERS")
public class User{

    @Id
    private String login;
    private String name;
    private String password;

    @ManyToMany(cascade = {CascadeType.MERGE})
    private Set<Role> roles = new HashSet<>();
Run Code Online (Sandbox Code Playgroud)

作用:

@Entity
@Table(name = "ROLE")
public class Role {

    @Id
    @Enumerated(value = EnumType.STRING)
    private RoleEnum name;

    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<>();
Run Code Online (Sandbox Code Playgroud)

用法

这种设置会很容易地添加/删除RoleUser.简单地通过使阵列,FE:user.getRoles().add(new Role("ADMIN"));mergeuser.删除通过传递空列表.

如果您忘记添加Role之前将其添加到用户,则很可能会出现如下错误:

javax.persistence.RollbackException: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: com.storage.entities.Role@246de37e.
Run Code Online (Sandbox Code Playgroud)

什么和为什么

如果选择在两个方向上映射关系,则必须将一个方向定义为所有者,另一个方向必须使用mappedBy属性来定义其映射(...)

级联EntityManager.merge()操作.如果在父项上调用merge(),则子项也将合并.这通常应该用于依赖关系.请注意,这只会影响合并的级联,关系引用本身将始终合并.