Java JPA坚持多对多

Java JPA坚持多对多,java,mysql,jpa,Java,Mysql,Jpa,我有一个非常标准的场景,其中我有一个以user_id作为主键的用户表和一个以role_id作为主键的角色表。这两个表通过多对多关系(即,用户可以有多个角色,一个角色可以应用于多个用户)进行关联,随后我有一个名为Users\u has\u roles的连接表。users\u has\u roles中只有两列是users\u user\u id和roles\u role\u id 我已经生成了实体类(见下文),将数据持久化到users和roles表中没有问题,但将任何内容持久化到users\u ha

我有一个非常标准的场景,其中我有一个以user_id作为主键的用户表和一个以role_id作为主键的角色表。这两个表通过多对多关系(即,用户可以有多个角色,一个角色可以应用于多个用户)进行关联,随后我有一个名为Users\u has\u roles的连接表。users\u has\u roles中只有两列是users\u user\u id和roles\u role\u id

我已经生成了实体类(见下文),将数据持久化到users和roles表中没有问题,但将任何内容持久化到users\u has\u roles joining表中失败了,因此目前没有为我的用户分配角色。在我发疯之前,能不能有人帮我摆脱痛苦,告诉我应该如何在users\u has\u roles表中添加一个users\u user\u id和一个对应的roles\u role\u id,这样我的用户就可以拥有角色了

My 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.
@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.
您的示例运行良好(EclipseLink 2.3,MySQL)。可能的问题是代码中未显示的部分。例如,在向角色集合添加元素时。例如,典型的错误是只向非拥有方添加元素

为了保持它,您必须关心拥有方(没有mappedBy的一方)中的关系,为了保持内存中的对象图与数据库一致,您应该始终修改关系的两侧

我尝试了以下方法:

    // 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();

好的,首先感谢Mikko引导我找到答案。我只是想发布一个答案,这个答案可能会直接帮助我所在职位上的其他人。另外,这是基于尤里卡时刻,所以它在技术上可能不正确,但这是我的看法

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

对我来说,最简单的方法是完全忘记桥接表,而专注于两个“真实”表,并将其中的数据关联起来。下面的代码不是最佳实践,因为我只是简单地设置了角色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);
List userRoleList=new ArrayList();
用户当前用户=新用户();
currentUser.setUserId(userId);
currentUser.setUsername(电子邮件);
currentUser.setPassword(密码);
Roles userRole=新角色();
userRole.setRoleId(“2”);
添加(userRole);
currentUser.SetRoleCollection(userRoleList);
getUsersFacade().create(当前用户);
希望这能帮助那些在多对多关系中挣扎的人


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

嗨,Mikko,我仍然感到困惑!我不明白上面的代码是如何工作的。如果我需要修改关系的两侧,我是否需要向RoleCollection和usersCollection添加元素?看起来您只是在添加角色集合。此外,我还不清楚我在各自的收藏中添加了什么。rolesCollection是否同时需要users\u user\u id和roles\u role\u id?问题是:要使其持久化到数据库,最低要求是修改拥有方(没有mappedBy的一方)。缺点是与mappedBy的集合不同步。这就是为什么通常最好修改两面,这意味着向rolesCollection和rolesCollection添加元素。好的,最佳做法是向这两个集合添加元素。。。但我应该在每个集合中添加什么?我是否同时添加用户id和角色id?您将添加实体本身,而不是id。假设您有用户u和角色r实体。要在它们之间创建关系,您需要将u添加到r的usersCollection,将r添加到u的RoleCollection。添加关系时,不必关心联接表中列的名称。不必添加新角色,过程与新建/现有类似。您不会使用实体的id将实体相互连接,而是通过将实体添加到集合并合并/持久化更改将实体连接到其他实体。1) 创建新用户2)从数据库获取现有角色3)按照前面的说明连接它们。4) 合并/持久化更改。
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);