Java 如何保持@ManyToMany 关系 - 重复条目或分离实体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9821914/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to persist @ManyToMany relation - duplicate entry or detached entity
提问by Aure77
I want to persist my entity with ManyToMany relation. But i have some problem during persisting process.
我想用 ManyToMany 关系保留我的实体。但是我在坚持过程中遇到了一些问题。
My entities :
我的实体:
@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
}
and
和
@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
}
Service code :
服务代码:
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
First time, when I try to persist a User with UserRoles "ROLE_USER", no problem. User and UserRoles and join tables are inserted.
第一次,当我尝试使用 UserRoles“ROLE_USER”持久化用户时,没问题。插入用户和用户角色以及连接表。
My problem is when I try to persist a second User with the same UserRoles. I check if the UserRoles exists by finding it (userRolesServices.getUserRoleByName(...)). If exists -> add this UserRoles to User list (id + role name) else i create a new one (only role name).
我的问题是当我尝试保留具有相同 UserRoles 的第二个用户时。我通过查找 UserRoles 来检查它是否存在(userRolesServices.getUserRoleByName(...))。如果存在 -> 将此 UserRoles 添加到用户列表(id + 角色名称),否则我创建一个新的(仅角色名称)。
By when I try to persist the second User, i obtain the following exception : "detached entity to persist : .....UserRoles"(maybe because getUserRoleByName is performed in another transaction)
当我尝试持久化第二个用户时,我获得以下异常: “要持久化的分离实体:.....UserRoles”(可能是因为 getUserRoleByName 在另一个事务中执行)
If I do not use getUserRoleByName(only *new UserRoles("ROLE_USER");*), i obtain the following exception : "...ConstraintViolation : Duplicated entry for 'ROLE_NAME' ..."
如果我不使用getUserRoleByName(仅 *new UserRoles("ROLE_USER");*),我会得到以下异常: “...ConstraintViolation:'ROLE_NAME' 的重复条目......”
So, how to properly persist an entity with @ManyToManyrelation ?
那么,如何正确持久化具有@ManyToMany关系的实体?
采纳答案by Amit Deshpande
For above problem I would say your entity relationship cascade is wrong. Consider this: A user can have multiple roles but there can be fixed number of roles that can exist in the system. So CASCADE ALL from User
entity does not make any sense, since life cycle of UserRoles
should not depend on User
entity life cycle. E.g. when we remove User
, UserRoles
should not get removed.
对于上述问题,我会说您的实体关系级联是错误的。考虑一下:一个用户可以有多个角色,但系统中可以存在固定数量的角色。所以 CASCADE ALL from User
entity 没有任何意义,因为生命周期不UserRoles
应该依赖于User
实体生命周期。例如,当我们删除时User
,UserRoles
不应该被删除。
detached entity to persistexception will only occur when you are passing object which has primary key already set to persist.
仅当您传递主键已设置为持久化的对象时,才会发生分离实体持久化异常。
Remove cascade and your problem will be solved now only thing you will need to decide is how you are going to insert User roles. According to me there should be separate functionality to do so.
删除级联,您的问题将得到解决,现在您只需要决定如何插入用户角色。在我看来,应该有单独的功能来做到这一点。
Also do not use ArrayList
, use HashSet
. ArrayList
allows duplicates.
也别用ArrayList
,用HashSet
。ArrayList
允许重复。
回答by James
(maybe because getUserRoleByName is performed in another transaction)
(可能是因为 getUserRoleByName 是在另一个事务中执行的)
That would seem to the the issue, do the query in the same transaction/entity manager. Otherwise re-find it in the current transaction using find().
这似乎是问题,在同一个事务/实体管理器中执行查询。否则使用 find() 在当前事务中重新查找它。
回答by Atais
I will provide my answer if anyone get same type of problem to me and the author.
如果有人遇到与我和作者相同类型的问题,我会提供我的答案。
Basically what I was facing was a situation when I had one table which was some kind of CONSTANTvalues. And the other would change, but it should map (many to many
) to those CONSTANTS.
基本上我面临的是一种情况,当我有一张表是某种CONSTANT值时。另一个会改变,但它应该将 ( many to many
)映射到那些CONSTANTS。
The exact problem is USERS
and it's ROLES
.
确切的问题是USERS
,它是ROLES
.
Roles
would be known and added on system startup, thus they should never get removed. Even if no user would have some Role
it should still be in the system.
Roles
将在系统启动时已知并添加,因此它们永远不应该被删除。即使没有用户会有一些Role
它应该仍然在系统中。
The class implementation, using JPA:
类实现,使用 JPA:
User:
用户:
@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<>();
Role:
角色:
@Entity
@Table(name = "ROLE")
public class Role {
@Id
@Enumerated(value = EnumType.STRING)
private RoleEnum name;
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
Usage
用法
This setup will easily add/remove Role
to User
. Simply by passing an array, f.e.: user.getRoles().add(new Role("ADMIN"));
and merge
the user
. Removing works with passing an empty list.
此设置将轻松添加/删除Role
到User
. 简单地通过使阵列,FE:user.getRoles().add(new Role("ADMIN"));
和merge
的user
。删除与传递空列表一起工作。
If you forget to add the Role
before adding it to the user most likely you will get an error like:
如果您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.
What and why
什么和为什么
mappedBy
attribute is added to the child Entity as described in the JPA Docs
mappedBy
属性被添加到子实体,如JPA 文档中所述
If you choose to map the relationship in both directions, then one direction must be defined as the owner and the other must use the mappedBy attribute to define itsmapping (...)
如果您选择在两个方向上映射关系,则必须将一个方向定义为所有者,而另一个方向必须使用 mappingBy 属性来定义其映射 (...)
cascade = {CascadeType.MERGE}
is added for proper cascades JPA Docs
cascade = {CascadeType.MERGE}
添加了适当的级联JPA Docs
Cascaded the EntityManager.merge() operation. If merge() is called on the parent, then the child will also be merged. This should normally be used for dependent relationships. Note that this only affects the cascading of the merge, the relationship reference itself will always be merged.
级联 EntityManager.merge() 操作。如果在父级上调用 merge(),则子级也将被合并。这通常应该用于依赖关系。请注意,这只影响合并的级联,关系引用本身将始终被合并。