运行以下代码时,我收到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异常。修复似乎是使用
显式定义两个Join表@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="Container_PrimaryElements")
List<Element> primaryElements;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="Container_SecondaryElements")
List<Element> secondaryElements;
似乎有用。
然而,我仍然想知道是否
答案 0 :(得分:1)
我可以想到两个可能的原因:
NOT NULL
关系表中REFERENCES
列的SECONDARYELEMENTS_ID
或CONTAINER_ELEMENT
约束不可延迟。在saveContainer()
调用期间,您将持久保存与非持久实体的关系。由于此关系是循环的(Element
与Container
相关,Element
与[...]}相关,因此无法通过重新排序来解决。我不确定h2如何处理这个问题,但在使用Postgres时我遇到了这个问题。
ID生成(在您的情况下)不适用于级联规则。出于某种原因,Element
没有获得生成的ID - 因此JPA会将其标识为NULL
(如果您的ELEMENT
表允许)。
如果您需要更多信息,请与我们联系。
来自你:
调试输出,尤其是生成的SQL语句(理想情况下是响应)有助于找出事务失败的时间点。在这种情况下,您的表定义(CREATE TABLE [...]
,尤其是约束定义)将有助于确定第一个原因是否可能是一个问题。
来自其他人:
对h2更有经验的人可以判断你是否需要一些&#34;魔法&#34; (比如在Postgres中使REFERENCES
可推迟)以插入具有循环关系的数据。
更新1 :是否需要指定@JoinTable,以防多个关系出现在同一类型的情况下?
可能的。规范有这个例子:
Entity Employee映射到名为EMPLOYEE的表。 实体专利被映射到名为PATENT的表。 有一个名为EMPLOYEE_PATENT(所有者名称优先)的连接表。这个连接表 有两个外键列。一个外键列是指表EMPLOYEE并且具有 与EMPLOYEE的主键相同的类型。此外键列已命名 EMPLOYEE_,其中表示的名称 表EMPLOYEE的主键列。其他外键列是指表 PATENT并且与PATENT的主键具有相同的类型。这个外键列是 名为PATENTS_,其中表示表PATENT的主键列的名称。
因此,表名是从实体名称派生而来的,它的列名是从关系目标列名和字段名中派生出来的。您的联接表有什么结构?如果它有两列以上,那就是你的问题。
更新2 :[...]此问题的更好解决方案?
取决于您的使用案例。您可以使用其他级别的继承,即PrimaryElement extends Element
和SecondaryElement extends Element
,然后使用单个字段List<Element> elements
来存储您的数据(您仍然可以查询特定类型)。当然,这只适用于Element
的类型是主要xor或辅助。
Otoh,使用两个连接表可能会更好,再次取决于您的用例(额外的JOIN与更大的表)。
为ManyToMany关系使用连接表&#34;应该&#34;实际工作(根据规范)
尝试删除primaryElements_id
和secondaryElements_id
的NOT NULL约束。