Hibernate Spring:@ManyToMany DataIntegrityViolationException ConstraintViolationException

时间:2013-10-16 17:31:04

标签: java spring hibernate many-to-many database-integrity

我正在为ManyToMany建立一个示例关系:User(1) - ()AccessLevel() - (1)角色

我用Java实现了3个带有hibernate实现的类,如下所示:

班级用户

@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;

Class 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;

问题:

当我使用merge方法持久化User 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属性,似乎hibernate也没有为AccessLevel生成主键。

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

此致 Cristian Colorado

2 个答案:

答案 0 :(得分:3)

<强>原因:

似乎对于ManyToMany关系,您不需要为“连接表”定义类。因此,如果我消除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);

重要的是我告诉hibernate用户实体将通过名为“access_level”的表与Role实体相关,并且这样的表将具有user_id和role_id列以便加入以前的实体。

到目前为止,这是所有hibernate需求,以便处理多对多的关系,因此当mergin使用该信息来创建和修改此脚本时:

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表的实体。
  • 我重写了我的生产脚本,以便拥有“access_level” 仅限user_id / role_id列。

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

答案 1 :(得分:0)

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

e.g。

USER_ID ROLE_ID date_granted

然后,您可能希望使用AccessLevel实体,但是将@ManyToMany替换为@OneToMany,从User替换为AccessLevel,也可以选择从Role&gt;替换。 ACCESSLEVEL。

Hibernate文档自己建议不要使用@ManyToMany:

不要使用异国情调的关联映射:

  

真正的多对多关联的实际测试用例很少见。最   当你需要存储在“链接”中的其他信息时   在这种情况下,使用两个一对多要好得多   与中间链接类的关联。事实上,大多数协会   是一对多和多对一的。出于这个原因,你应该继续   使用任何其他协会风格时要小心。