java - 使用多个ManyToMany关系时的org.hibernate.exception.ConstraintViolationException

标签 java spring hibernate jpa many-to-many

运行以下代码时,我收到 org.hibernate.exception.ConstraintViolationException 异常

提示

org.h2.jdbc.JdbcSQLException: NULL not allowed for column "SECONDARYELEMENTS_ID";

我知道这是由于从 Container 对象到 Element 对象有两个 @ManyToMany 关系引起的。如果我删除

@ManyToMany(cascade = CascadeType.ALL)
List<Element> secondaryElements;

从 Container 类来看,一切都运行良好。

我在这里缺少什么?

如果您需要更多信息,请告诉我。

@Transactional
public class JPA2Runner {
     //hidding Spring Data JPA repository
     @Autowired 
     ContainerDAO containerDAO;
     public boolean run() throws Exception{
         Container container1 = new Container( );
         Container container2 = new Container( );
         Element element1 = new Element( container1, container2);
         Element element2 = new Element( container2, container1);
         container1.getPrimaryElements().add(element1);
         container1.getSecondaryElements().add(element2);
         container2.getPrimaryElements().add(element2);
         container2.getSecondaryElements().add(element1);
         containerDAO.saveContainer(container1);
         return true;
     }
}

@Entity
public class Container extends AbstractEntity {          
    @ManyToMany(cascade = CascadeType.ALL)
    List<Element> primaryElements;
    @ManyToMany(cascade = CascadeType.ALL)
    List<Element> secondaryElements;

    public Container( ){
        primaryElements =new ArrayList<Element>();
        secondaryElements = new ArrayList<Element>();
    }
}

@Entity
public class Element extends AbstractEntity {
    @ManyToOne(cascade = CascadeType.ALL)
    private Container dedicatedContainer1;
    @ManyToOne(cascade = CascadeType.ALL)
    private Container dedicatedContainer2;

    public Element(){}      
    public Element(Container container1, Container container2){
        this.dedicatedContainer1 = container1;
        this.dedicatedContainer2 = container2;
    }
}

更新 1: 难道同一个类型有多个关系,需要指定@JoinTable吗?

更新 2: 感谢@ducksteps 的提示和评论,我能够找到解决该问题的方法。 问题是上面的定义生成了一个带有两个元素列表的键的连接表,即

create table Container_Element (Container_id bigint not null, secondaryElements_id bigint not null, primaryElements_id bigint not null)

但是,保存容器会在连接表中生成以下插入内容

 insert into Container_Element (Container_id, primaryElements_id) values (?, ?)

这会导致 ConstraintViolation 异常。修复似乎是使用显式定义两个连接表

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="Container_PrimaryElements")
List<Element> primaryElements;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="Container_SecondaryElements")
List<Element> secondaryElements;

这似乎有效。

但是,我仍然想知道是否

  • 这个问题有更好的解决方案吗?
  • 对 ManyToMany 关系使用联接表“应该”实际有效(根据规范)?

最佳答案

我可以想到两个可能的原因:

  1. NOT NULLREFERENCES SECONDARYELEMENTS_ID 的约束CONTAINER_ELEMENT 中的列关系表不可推迟。期间saveContainer()调用,您可以持久保存与非持久实体的关系。由于这种关系是循环的(Element 涉及 Container 涉及 Element 涉及 [...]),因此无法通过重新排序来解决。我不确定 h2 如何处理这个问题,但我在使用 Postgres 时遇到了这个问题。

  2. ID 生成(在您的情况下)不适用于级联规则。由于某种原因,Element没有获得生成的 ID - 因此 JPA 将其保留为 ID NULL (如果您的 ELEMENT 表允许)。

Let me know if you need more information.

来自您:

调试输出,尤其是生成的 SQL 语句(最好带有响应)将有助于找出事务在哪个点失败。您的表定义( CREATE TABLE [...] ,尤其是约束定义)对于查明第一个原因是否可能是这种情况下的问题很有用。

来自其他人:

对 h2 更有经验的人可以告诉你是否需要一些“魔法”(例如在 Postgres 中使 REFERENCES 可延迟)来插入具有循环关系的数据。

Update 1: Could it be that it is required to specify the @JoinTable in case there are multiple relations to the same type?

有可能。规范中有以下示例:

Entity Employee is mapped to a table named EMPLOYEE. Entity Patent is mapped to a table named PATENT. There is a join table that is named EMPLOYEE_PATENT (owner name first). This join table has two foreign key columns. One foreign key column refers to table EMPLOYEE and has the same type as the primary key of EMPLOYEE. This foreign key column is named EMPLOYEE_, where denotes the name of the primary key column of table EMPLOYEE.The other foreign key column refers to table PATENT and has the same type as the primary key of PATENT. This foreign key column is named PATENTS_, where denotes the name of the primary key column of table PATENT.

因此,表名是从实体名称派生的,它的列名是从关系目标列名和字段名派生的。您的连接表有什么结构?如果它有两列以上,那就是你的问题了。

Update 2: [...] better solutions for this issue?

取决于您的用例。您可以使用另一个继承级别,即 PrimaryElement extends ElementSecondaryElement extends Element然后使用单个字段 List<Element> elements存储您的数据(您仍然可以查询特定类型)。当然,这只适用于 Element 的类型。是主要的异或次要的。

Otoh,使用两个联接表甚至可能效果更好,这同样取决于您的用例(额外的联接与更大的表)。

using a join table for both ManyToMany relations "should" actually work (according to the specification)

尝试删除 primaryElements_id 的 NOT NULL 约束和secondaryElements_id .

关于java - 使用多个ManyToMany关系时的org.hibernate.exception.ConstraintViolationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29526931/

相关文章:

java - Eclipse Java代码格式

Spring mvc 应用程序在 tomcat 中不能作为 war 运行,但在 IDE 中运行良好

java - JSON元素从单个变为列表

java - 如何在 Hibernate/JPA 中使用 xml 编写命名查询?

java - 通过循环从套接字读取数据时获取 "java.io.EOFException"

java - SQL排序有限制吗? (不平凡)

java - 如何在 java 模式下配置 emacs,使其不会自动对齐方法参数

java - 没有可用的事务性 EntityManager - 使用 JPA Api,Hibernate Session 出错

java - 无法 Autowiring 。找不到 ... 类型的 bean

java - 在对象的同一个表中显示 OneToMany 列表的大小?