java JPA 坚持多对多

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10266390/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 00:18:08  来源:igfitidea点击:

JPA persist many to many

javamysqljpa

提问by tarka

I have a pretty standard scenario whereby I have a table of Users with user_id as the PK and a table of Roles with role_id as the PK. The two tables are related via a many to many relationship (ie. Users can have many roles and a role can be applied to many users) and subsequently I have a joining table called users_has_roles. The only two columns in users_has_roles are users_user_id and roles_role_id.

我有一个非常标准的场景,其中我有一个以 user_id 作为 PK 的用户表和一个以 role_id 作为 PK 的角色表。这两个表通过多对多关系相关联(即用户可以有多个角色,一个角色可以应用于多个用户),随后我有一个名为 users_has_roles 的连接表。users_has_roles 中仅有的两列是 users_user_id 和 roles_role_id。

I have generated the entity classes (see below) and I have no problem persisting data to the users and roles tables but I have failed miserably persist anything to the users_has_roles joining table so currently none of my users are being assigned a role. Before I go crazy could somebody put me out of my misery and show me how I should go about adding a users_user_id with a corresponding roles_role_id to the users_has_roles table so my users can have roles?

我已经生成了实体类(见下文)并且我没有问题将数据持久化到用户和角色表,但是我失败了,将任何东西持久化到 users_has_roles 连接表,所以目前我的用户都没有被分配角色。在我发疯之前,有人能让我摆脱痛苦,告诉我应该如何将带有相应角色角色 ID 的 users_user_id 添加到 users_has_roles 表,以便我的用户可以拥有角色吗?

My Users.java entity class:

我的 Users.java 实体类:

@Entity
@Table(name = "users")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "Users.findAll", query = "SELECT u FROM Users u"),
@NamedQuery(name = "Users.findByUserId", query = "SELECT u FROM Users u WHERE u.userId = :userId"),
@NamedQuery(name = "Users.findByUsername", query = "SELECT u FROM Users u WHERE u.username = :username"),
@NamedQuery(name = "Users.findByPassword", query = "SELECT u FROM Users u WHERE u.password = :password")})
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 60)
@Column(name = "user_id")
private String userId;
@Basic(optional = false)
@NotNull
@Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")
@Size(min = 1, max = 45)
@Column(name = "username")
private String username;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 120)
@Column(name = "password")
private String password;
@JoinTable(name = "users_has_roles", joinColumns = {
    @JoinColumn(name = "users_user_id", referencedColumnName = "user_id")}, inverseJoinColumns = {
    @JoinColumn(name = "roles_role_id", referencedColumnName = "role_id")})
@ManyToMany
private Collection<Roles> rolesCollection;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "usersUserId")
private Collection<UserAccount> userAccountCollection;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "usersUserId")
private Collection<UserDetails> userDetailsCollection;

...

All the getter and setter methods etc.

My Roles.java entity class:

我的 Roles.java 实体类:

@Entity
@Table(name = "roles")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "Roles.findAll", query = "SELECT r FROM Roles r"),
@NamedQuery(name = "Roles.findByRoleId", query = "SELECT r FROM Roles r WHERE r.roleId = :roleId"),
@NamedQuery(name = "Roles.findByRoleName", query = "SELECT r FROM Roles r WHERE r.roleName = :roleName"),
@NamedQuery(name = "Roles.findByRolePermission", query = "SELECT r FROM Roles r WHERE r.rolePermission = :rolePermission"),
@NamedQuery(name = "Roles.findByRoleDescription", query = "SELECT r FROM Roles r WHERE r.roleDescription = :roleDescription")})
public class Roles implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 60)
@Column(name = "role_id")
private String roleId;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 45)
@Column(name = "role_name")
private String roleName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 45)
@Column(name = "role_permission")
private String rolePermission;
@Size(max = 45)
@Column(name = "role_description")
private String roleDescription;
@ManyToMany(mappedBy = "rolesCollection")
private Collection<Users> usersCollection;

...

All the getter and setter methods etc.

Thanks

谢谢

---- UPDATE ----

- - 更新 - -

// New Users
Users currentUser = new Users();
currentUser.setUserId(userId);
currentUser.setUsername(email);
currentUser.setPassword(password);
getUsersFacade().create(currentUser);

回答by tarka

Ok first off thanks to Mikko for leading me to the answer. I just wanted to post an answer that might be directly helpful to anybody else that might be in the position I was in. Also this is based on a Eureka moment so it might not be technically correct but this is how I see it.

好的,首先感谢 Mikko 带领我找到答案。我只是想发布一个可能对处于我所处位置的其他任何人直接有帮助的答案。此外,这是基于尤里卡时刻,所以它在技术上可能不正确,但这就是我的看法。

The big issue that I faces was that in MySQL I could see the bridging table as an individual table! (sorry I can't post an image of my EER diagram but I don't seem to have enough privileges at the moment) So I assumed that Java would also see the bridging table as a table! Well it doesn't. That bridging table doesn't really exist in Java as a conventional table it is in fact represented by the opposing tables collection type that you associate with it.

我面临的最大问题是在 MySQL 中我可以将桥接表视为单个表!(抱歉,我无法发布我的 EER 图的图像,但我目前似乎没有足够的权限)所以我假设 Java 也会将桥接表视为表!好吧,它没有。该桥接表在 Java 中实际上并不作为传统表存在,它实际上是由与之关联的对立表集合类型表示的。

The easiest way to see it for me was to completely forget the bridging table and concentrate on the two 'real' tables and associating the data in those. The following code is NOT best practice as I'm simply setting the role_id but it's fine just to show my point.

对我来说,查看它的最简单方法是完全忘记桥接表并专注于两个“真实”表并将其中的数据关联起来。以下代码不是最佳实践,因为我只是设置了 role_id,但只是为了表明我的观点也很好。

List<Roles> userRoleList = new ArrayList<Roles>();

Users currentUser = new Users();
currentUser.setUserId(userId);
currentUser.setUsername(email);
currentUser.setPassword(password);

Roles userRole = new Roles();
userRole.setRoleId("2");

userRoleList.add(userRole);
currentUser.setRolesCollection(userRoleList);

getUsersFacade().create(currentUser);

Hope that helps anybody else that is struggling with many to many relationships.

希望能帮助其他在多对多关系中挣扎的人。

(NB. I've edited the original question code to use a List instead of a Collection for ease but you can just as well use any other type that fits your needs.)

(注意。为了方便起见,我编辑了原始问题代码以使用列表而不是集合,但您也可以使用适合您需要的任何其他类型。)

回答by Mikko Maunu

Your example works fine (EclipseLink 2.3, MySQL). Likely problem is in part of the code that you do not show. For example in adding element to rolesCollection. Typical mistake is for example to add element only to the non owning side.

您的示例工作正常(EclipseLink 2.3,MySQL)。可能的问题是您未显示的部分代码。例如在向rolesCollection 添加元素时。典型的错误是例如只向非拥有方添加元素。

For persisting it you have to keep care about relation in owning side (one without mappedBy), for keeping also in-memory object graph consistent with database, you should always modify both sides of relation.

为了持久化它,你必须关心拥有方的关系(一个没有mappedBy),为了保持内存中的对象图与数据库一致,你应该总是修改关系的双方。

I tried it with following:

我尝试了以下方法:

    // method to add element to rolesCollection
    public void addRoles(Roles r) {
        rolesCollection.add(r);
    }

    //first created new instances of Users and Roles
    // then:
    tx.begin();
    users.addRoles(r);
    //it is not needed for persisting, but here you can also
    //add user to roles.
    em.persist(users);
    em.persist(r);
    tx.commit();