2

我正在为以下之间的多对多关系构建示例:User(1) - ( )AccessLevel( ) - (1)Role

我已经在 J​​ava 中实现了 3 个具有休眠实现的类,如下所示:

类用户

@Entity
@Table(name="user")
public class User {
    @Id
    @GeneratedValue
    @Column(name="USER_ID")

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "access_level", joinColumns = { 
            @JoinColumn(name = "user_id", nullable = false, updatable = false) }, 
            inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false, updatable = false) })
    private Set<Role> roles = new HashSet<Role>(0);

类角色

@Entity
@Table(name="role")
public class Role {

    @Id
    @GeneratedValue
    @Column(name="role_id")
    private Integer id;

    @Column(name="role_name")
    private String roleName;

类访问级别

@Entity
@Table(name="access_level")
public class AccessLevel {

    @Id
    @GeneratedValue
    private Integer id;
    @Column(name="role_id")
    private Integer  roleId;
    @Column(name="user_id")
    private Integer  userId;

问题:

当我使用合并方法持久化用户 bean 时,会出现异常:

@Service
public class UserServiceImpl implements UserService {

    @PersistenceContext
    private EntityManager em;

    @Override
    @Transactional
    public void save(User user) {
        em.merge(user);
    }

例外

org.springframework.web.util.NestedServletException:请求过程

ing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; SQL [insert into access_level (user_id, role_id) values (?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

如您所见,hibernate 正在尝试运行此查询:

insert into access_level (user_id, role_id) values (?, ?)

从我的角度来看,即使我已将 @GeneratedValue 添加到 id 属性,休眠似乎也没有为 AccessLevel 生成主键。

注意: 我正在使用 Postgresql 开发生产环境,使用 HSQL 数据库开发环境,该数据库从一开始就根据实体描述创建所有模式。两种环境都会产生相同的问题。

问候,克里斯蒂安·科罗拉多

4

2 回答 2

3

原因:

对于多对多关系,您似乎不需要为“联接表”定义一个类。因此,如果我消除 AccessLevel 实体,逻辑将完全正常。我进一步解释:

解释:

当我定义 User 类时,我还描述了与 Role 的关系:

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "access_level", joinColumns = { 
            @JoinColumn(name = "user_id", nullable = false, updatable = false) }, 
            inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false, updatable = false) })
    private Set<Role> roles = new HashSet<Role>(0);

这里重要的是我告诉休眠用户实体将通过一个名为“access_level”的表与角色实体相关联,并且此类表将具有 user_id 和 role_id 列,以便加入以前的实体。

到目前为止,为了处理多对多关系,这是所有 hibernate 需要的,因此在合并时它使用该信息来创建和调整这个脚本:

insert into access_level (user_id, role_id) values (?, ?)

现在,当我为 AccessLevel 定义一个新实体时,问题就来了:

@Entity
    @Table(name="access_level")
    public class AccessLevel {

        @Id
        @GeneratedValue
        private Integer id;
        @Column(name="role_id")
        private Integer  roleId;
        @Column(name="user_id")
        private Integer  userId;

现在我告诉 hibernate 有一个与 AccessLevel 实体相关的表“access_level”,它有 3 列,最重要的是 Id,它是主键。

所以我定义了“access_level”两次!

解决方案:

  • 我消除了 access_level 表的实体。
  • 我重新编写了我的生产脚本,以便只有 user_id/role_id 列的“access_level”。

注意:最好知道如何将主键添加到连接表而不产生问题。另一种方法是在数据库中添加一个组合主键(user_id/role_id),它独立于休眠。

于 2013-10-16T17:31:03.547 回答
0

为什么连接表中需要一个PK列?会有一个由 user_id 和 role_id 组成的复合 PK。现在,正如您发现 @ManyToMany 的 JoinTable 将永远只有两列,并且在某些时候您可能需要有关此关系的其他数据。

例如

user_id role_id date_granted

然后,您可能想要使用您的 AccessLevel 实体,但是您将 @ManyToMany 替换为 @OneToMany 从 User 到 AccessLevel,并且可以选择从 Role > AccessLevel。

Hibernate 文档自己反对@ManyToMany:

不要使用奇异的关联映射:

真正多对多关联的实际测试用例很少见。大多数时候,您需要存储在“链接表”中的附加信息。在这种情况下,最好使用两个一对多关联到一个中间链接类。事实上,大多数关联都是一对多和多对一的。因此,在使用任何其他关联样式时应谨慎行事。

于 2013-10-16T22:08:06.607 回答