Java 如何删除 JPA 中具有 ManyToMany 关系的实体(以及相应的连接表行)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1082095/
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 remove entity with ManyToMany relationship in JPA (and corresponding join table rows)?
提问by rdk
Let's say I have two entities: Group and User. Every user can be member of many groups and every group can have many users.
假设我有两个实体:组和用户。每个用户可以是多个组的成员,每个组可以有多个用户。
@Entity
public class User {
@ManyToMany
Set<Group> groups;
//...
}
@Entity
public class Group {
@ManyToMany(mappedBy="groups")
Set<User> users;
//...
}
Now I want to remove a group (let's say it has many members).
现在我想删除一个组(假设它有很多成员)。
Problem is that when I call EntityManager.remove() on some Group, JPA provider (in my case Hibernate) does not remove rows from join tableand delete operation fails due to foreign key constrains. Calling remove() on User works fine (I guess this has something to do with owning side of relationship).
问题是,当我在某个组上调用 EntityManager.remove() 时,JPA 提供程序(在我的情况下为 Hibernate)不会从连接表中删除行,并且由于外键约束,删除操作失败。在 User 上调用 remove() 工作正常(我想这与拥有关系的一面有关)。
So how can I remove a group in this case?
那么在这种情况下如何删除组?
Only way I could come up with is to load all users in the group, then for every user remove current group from his groups and update user. But it seems ridiculous to me to call update() on every user from the group just to be able to delete this group.
我能想出的唯一方法是加载组中的所有用户,然后为每个用户从他的组中删除当前组并更新用户。但对我来说,对组中的每个用户调用 update() 只是为了能够删除这个组,这似乎很荒谬。
采纳答案by Grzegorz Oledzki
- The ownership of the relation is determined by where you place the 'mappedBy' attribute to the annotation. The entity you put 'mappedBy' is the one which is NOT the owner. There's no chance for both sides to be owners. If you don't have a 'delete user' use-case you could simply move the ownership to the
Group
entity, as currently theUser
is the owner. - On the other hand, you haven't been asking about it, but one thing worth to know. The
groups
andusers
are not combined with each other. I mean, after deleting User1 instance from Group1.users, the User1.groups collections is not changed automatically (which is quite surprising for me), - All in all, I would suggest you decide who is the owner. Let say the
User
is the owner. Then when deleting a user the relation user-group will be updated automatically. But when deleting a group you have to take care of deleting the relation yourself like this:
- 关系的所有权取决于将“mappedBy”属性放置到注释的位置。您放置“mappedBy”的实体不是所有者。双方都没有机会成为所有者。如果您没有“删除用户”用例,您可以简单地将所有权移至
Group
实体,因为当前User
是所有者。 - 另一方面,你没有问过它,但有一件事值得知道。的
groups
和users
不与彼此组合。我的意思是,从 Group1.users 中删除 User1 实例后,User1.groups 集合不会自动更改(这对我来说非常令人惊讶), - 总而言之,我建议您决定谁是所有者。假设
User
是所有者。然后当删除用户时,关系用户组将自动更新。但是在删除组时,您必须像这样自己删除关系:
entityManager.remove(group)
for (User user : group.users) {
user.groups.remove(group);
}
...
// then merge() and flush()
回答by jelies
I found a possible solution, but... I don't know if it's a good solution.
我找到了一个可能的解决方案,但是......我不知道这是否是一个好的解决方案。
@Entity
public class Role extends Identifiable {
@ManyToMany(cascade ={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable(name="Role_Permission",
joinColumns=@JoinColumn(name="Role_id"),
inverseJoinColumns=@JoinColumn(name="Permission_id")
)
public List<Permission> getPermissions() {
return permissions;
}
public void setPermissions(List<Permission> permissions) {
this.permissions = permissions;
}
}
@Entity
public class Permission extends Identifiable {
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable(name="Role_Permission",
joinColumns=@JoinColumn(name="Permission_id"),
inverseJoinColumns=@JoinColumn(name="Role_id")
)
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
I have tried this and it works. When you delete Role, also the relations are deleted (but not the Permission entities) and when you delete Permission, the relations with Role are deleted too (but not the Role instance). But we are mapping a unidirectional relation two times and both entities are the owner of the relation. Could this cause some problems to Hibernate? Which type of problems?
我试过这个,它的工作原理。当您删除 Role 时,关系也会被删除(但不是 Permission 实体),当您删除 Permission 时,与 Role 的关系也会被删除(但不是 Role 实例)。但是我们两次映射一个单向关系,并且两个实体都是关系的所有者。这会导致 Hibernate 出现一些问题吗?哪类问题?
Thanks!
谢谢!
The code above is from another postrelated.
上面的代码来自另一个相关的帖子。
回答by NBW
For what its worth, I am using EclipseLink 2.3.2.v20111125-r10461 and if I have a @ManyToMany unidirectional relationship I observe the problem that you describe. However, if I change it to be a bi-directional @ManyToMany relationship I am able to delete an entity from the non-owning side and the JOIN table is updated appropriately. This is all without the use of any cascade attributes.
就其价值而言,我使用的是 EclipseLink 2.3.2.v20111125-r10461,如果我有 @ManyToMany 单向关系,我会观察到您描述的问题。但是,如果我将其更改为双向 @ManyToMany 关系,我可以从非拥有方删除一个实体,并相应地更新 JOIN 表。这一切都没有使用任何级联属性。
回答by damian
The following works for me. Add the following method to the entity that is not the owner of the relationship (Group)
以下对我有用。将以下方法添加到不是关系所有者的实体(组)
@PreRemove
private void removeGroupsFromUsers() {
for (User u : users) {
u.getGroups().remove(this);
}
}
Keep in mind that for this to work, the Group must have an updated list of Users (which is not done automatically). so everytime you add a Group to the group list in User entity, you should also add a User to the user list in the Group entity.
请记住,要使其正常工作,组必须具有更新的用户列表(这不会自动完成)。所以每次你在 User 实体的 group 列表中添加一个 Group 时,你也应该在 Group 实体的用户列表中添加一个 User。
回答by Pierre Henry
As an alternative to JPA/Hibernate solutions : you could use a CASCADE DELETE clause in the database definition of your foregin key on your join table, such as (Oracle syntax) :
作为 JPA/Hibernate 解决方案的替代方案:您可以在连接表上的外键的数据库定义中使用 CASCADE DELETE 子句,例如(Oracle 语法):
CONSTRAINT fk_to_group
FOREIGN KEY (group_id)
REFERENCES group (id)
ON DELETE CASCADE
That way the DBMS itself automatically deletes the row that points to the group when you delete the group. And it works whether the delete is made from Hibernate/JPA, JDBC, manually in the DB or any other way.
这样,当您删除组时,DBMS 本身会自动删除指向该组的行。无论删除是从 Hibernate/JPA、JDBC、在 DB 中手动进行还是以任何其他方式进行,它都可以工作。
the cascade delete feature is supported by all major DBMS (Oracle, MySQL, SQL Server, PostgreSQL).
所有主要的 DBMS(Oracle、MySQL、SQL Server、PostgreSQL)都支持级联删除功能。
回答by user1776955
This is a good solution. The best part is on the SQL side – fine tuning to any level is easy.
这是一个很好的解决方案。最好的部分是在 SQL 端——任何级别的微调都很容易。
I used MySql and MySql Workbench to Cascade on delete for the Required Foreign KEY.
我使用 MySql 和 MySql Workbench 来级联删除所需的外键。
ALTER TABLE schema.joined_table
ADD CONSTRAINT UniqueKey
FOREIGN KEY (key2)
REFERENCES schema.table1 (id)
ON DELETE CASCADE;
回答by Mehul Katpara
This works for me:
这对我有用:
@Transactional
public void remove(Integer groupId) {
Group group = groupRepository.findOne(groupId);
group.getUsers().removeAll(group.getUsers());
// Other business logic
groupRepository.delete(group);
}
Also, mark the method @Transactional (org.springframework.transaction.annotation.Transactional), this will do whole process in one session, saves some time.
另外,标记方法@Transactional (org.springframework.transaction.annotation.Transactional),这将在一个会话中完成整个过程,节省一些时间。
回答by szachMati
For my case, I deleted the mappedBy and joined tables like this:
对于我的情况,我删除了mappedBy并加入了这样的表:
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "user_group", joinColumns = {
@JoinColumn(name = "user", referencedColumnName = "user_id")
}, inverseJoinColumns = {
@JoinColumn(name = "group", referencedColumnName = "group_id")
})
private List<User> users;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JsonIgnore
private List<Group> groups;
回答by Me Me
This works for me on a similar issue where I failed to delete the user due to the reference. Thank you
这对我在一个类似的问题上有用,我由于引用而未能删除用户。谢谢
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST,CascadeType.REFRESH})