JPA多对多关系持久化到关联表

6

我有两张表,它们之间是多对多的关系。当我持久化用户时,我将这两个实体映射起来,不会向连接表中插入任何内容。我一直在调试到持久化,我看到组列表不为null。

没有错误消息,只是持久化了用户。

用户 <--> 用户-组 <--> 组

我正在使用Netbeans 7.3、Glassfish 3.1.2.2、PostgreSQL 9.1和EclipseLink 2。

另外,我尝试显示SQL脚本,但下面的属性对我无效。

        <property name="eclipselink.logging.logger" value="ServerLogger"/>
        <property name="eclipselink.logging.level" value="FINE"/>
        <property name="eclipselink.logging.level.sql" value="FINE"/>
        <property name="eclipselink.logging.parameters" value="true"/>

抽象 DAO:

public abstract class GenericDAO<E> implements Serializable{

@PersistenceContext
EntityManager entityManager;

public void persist(E object){
    entityManager.persist(object);
}

public void merge(E object){
    entityManager.merge(object);
}

public void delete(E object){
    object = entityManager.merge(object);
    entityManager.remove(object);
}
}

用户实体:

   @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.findByName", query = "SELECT u FROM Users u WHERE u.name = :name"),
    @NamedQuery(name = "Users.findBySurname", query = "SELECT u FROM Users u WHERE u.surname = :surname"),
    @NamedQuery(name = "Users.findByEmail", query = "SELECT u FROM Users u WHERE u.email = :email"),
    @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
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "user_id")
    private Integer userId;
    @Size(max = 2147483647)
    @Column(name = "name")
    private String name;
    @Size(max = 2147483647)
    @Column(name = "surname")
    private String surname;
    // @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")//if the field contains email address consider using this annotation to enforce field validation
    @Size(max = 2147483647)
    @Column(name = "email")
    private String email;
    @Size(max = 2147483647)
    @Column(name = "username")
    private String username;
    @Size(max = 2147483647)
    @Column(name = "password")
    private String password;
    @ManyToMany(mappedBy = "usersList")
    private List<Groups> groupsList;
    @OneToMany(mappedBy = "userId")
    private List<Person> personList;

//Getters Setters

群组实体:

@Entity
@Table(name = "groups")
@XmlRootElement
@NamedQueries(
{
    @NamedQuery(name = "Groups.findAll", query = "SELECT g FROM Groups g"),
    @NamedQuery(name = "Groups.findByGroupId", query = "SELECT g FROM Groups g WHERE g.groupId = :groupId"),
    @NamedQuery(name = "Groups.findByGroupName", query = "SELECT g FROM Groups g WHERE g.groupName = :groupName"),
    @NamedQuery(name = "Groups.findByGroupDescription", query = "SELECT g FROM Groups g WHERE g.groupDescription = :groupDescription")
})
public class Groups implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "group_id")
    private Integer groupId;
    @Size(max = 2147483647)
    @Column(name = "group_name")
    private String groupName;
    @Size(max = 2147483647)
    @Column(name = "group_description")
    private String groupDescription;
    @JoinTable(name = "user_group", joinColumns =
    {
        @JoinColumn(name = "group_id", referencedColumnName = "group_id")
    }, inverseJoinColumns =
    {
        @JoinColumn(name = "user_id", referencedColumnName = "user_id")
    })
    @ManyToMany(fetch = FetchType.EAGER)
    private List<Users> usersList;
//Getters Setters

用户 DAO:

public class UserDAO extends GenericDAO<Users> implements Serializable {

    public List<Users> getAllUsers()
    {
        Query query = entityManager.createNamedQuery("Users.findAll");
        List<Users> users = query.getResultList();
        return users;
    }
}
2个回答

7

在使用PERSIST或者所有操作中,需要启用级联合并(cascade)。

@ManyToMany(mappedBy = "usersList", cascade = CascadeType.PERSIST)
private List<Groups> groupsList;

如果我设置CascadeType.PERSIST,则它也会向groups表中插入数据。我想要将数据添加到users表和user_group表中(pk_user,pk_group)。映射/连接表的工作方式是user_group必须在user和group表上具有外键约束。这就是为什么必须插入group的新行以使用其主键向user_group添加新行的原因。这与JPA无关,即使您使用纯JDBC也是如此。这是数据库中实体关系的工作方式。
另外,我删除了所有表格,它们由Eclipse Link自动生成。它们相同,但不将任何行插入'user_group'。这种行为由指定为persistence-unit的eclipselink.ddl-generation属性控制。当指定为drop-and-create-tables时,EclipseLink会重新创建整个数据库模式(在此过程中删除任何现有数据)。
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

然而,启用此功能只是为了方便您的开发。这不应该在生产环境中使用,因为通过指定此属性或将其值设置为none来禁用它。

<property name="eclipselink.ddl-generation" value="none"/>

如果我设置CascadeType.PERSIST,它也会将数据插入到groups表中。我想要将数据添加到users表和user_group表中(pk_user,pk_group)。 - erdoganonur
这非常不寻常。user_group表将依赖于group表的外键约束。这就是为什么首先插入group表,然后使用其主键添加新行到user_group表中。否则将无法完成此操作。 - Ravi K Thapliyal
'user_group'表与'group'和'user'表有外键关联。同时,我已经删除了所有的表格,这些表格是由Eclipse Link自动生成的。虽然它们相同,但不会向'user_group'插入任何行。 - erdoganonur
'user_group'表与'users'和'groups'有外键。我了解JPA在级联策略为persist时的行为,但我仍然无法处理它。您可以在git上检查我的项目:https://github.com/erdoganonur/MkmTurizm。感谢您的帮助。 - erdoganonur

1

关于如何显示 SQL 脚本的问题,我已经在 persistence.xml 文件中添加了以下代码:

<properties>
  <property name="eclipselink.logging.level.sql" value="FINE"/>
  <property name="eclipselink.logging.parameters" value="true"/>
</properties>

之后,进行清理、构建和部署。JPA执行的SQL将显示在NetBeans的GlassFish域日志中。

对于你提出的关于多对多关系的第二个问题。

我在同样的情况下遇到了同样的问题,因为我需要实现Web安全性并且需要使用相同的关系。不管怎样,我发现问题不在实体中。

你需要检查(对于我的情况)UserJPAController.java中的插入和编辑方法,这些方法知道如何引用彼此的实体,所以我实现了这段代码,它可以正常工作。

List<Grupo> attachedGrupoList = new ArrayList<Grupo>();
        for (Grupo grupoListGrupoToAttach : usuario.getGrupoList()) {
            grupoListGrupoToAttach = em.getReference(grupoListGrupoToAttach.getClass(),      grupoListGrupoToAttach.getIdGrupo());
            attachedGrupoList.add(grupoListGrupoToAttach);
        }
        usuario.setGrupoList(attachedGrupoList);
        em.persist(usuario);
for (Grupo grupoListGrupo : usuario.getGrupoList()) {
            grupoListGrupo.getUsuarioList().add(usuario);
            grupoListGrupo = em.merge(grupoListGrupo);
        }

执行该代码并且事务结束后,您将会在数据库中看到由JPA执行的SQL脚本。

希望能对您有所帮助!

Mike


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接