我是JPA的新手,我遇到了以下问题,可能是由于对级联和关联的理解不充分? 我有两个实体,它们是双向的OneToMany - ManyToOne关联:
@Entity
public class TestRun implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId() {
return id;
}
public TestRun(){
}
public void setId(Long id) {
this.id = id;
}
@ManyToOne(optional=false, cascade={CascadeType.PERSIST, CascadeType.MERGE})
private Test performedTest;
public Test getPerformedTest() {
return performedTest;
}
public void setPerformedTest(Test performedTest) {
this.performedTest = performedTest;
}
.....
@Override
public boolean equals(Object object) {
if (!(object instanceof TestRun)) {
return false;
}
TestRun other = (TestRun) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "TestRun[ id=" + id + " ]";
}
}
并且:
@Entity
public class Test implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Basic(optional=false)
@Column(nullable=false)
private String name;
@OneToMany(mappedBy="performedTest", cascade={CascadeType.MERGE, CascadeType.PERSIST})
private Collection<TestRun> appearsOnTestRuns;
public Collection<TestRun> getAppearsOnTestRuns() {
return appearsOnTestRuns;
}
public void setAppearsOnTestRuns(Collection<TestRun> appearsOnTestRuns) {
this.appearsOnTestRuns = appearsOnTestRuns;
}
//id based equals and hashcode
...
}
这种关系意味着我有一组Test-s,它们多次执行,每次都与TestRun相关联。 要创建TestRun,我使用以下方法:
public void createTestRun(String name){
Test testPerformed = ejb.findByName(name); //Returns null iff forTest is not in the DB--> INSERT new Test
if (testPerformed == null){
testPerformed = EntityFactory.newTest(name);//I set the name for test
}
TestRun run = EntityFactory.newTestRun(testPerformed);
ejb.persist(run);
}
public class EntityFactory{
public static TestRun newTestRun(Test test){
TestRun run = new TestRun();
run.setPerformedTest(test);
return run;
}
public static Test newTest(String name){
Test test = new Test();
test.setName(name);
return test;
}
}
当createTestRun处理新的Test(插入)时,Test对象被正确保存以及TestRun。但是当测试已经存在时,JPA(在我的情况下是EclipseLink)无论如何都会尝试执行INSERT:
....
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL110705102925930' defined on 'TEST'.
Error Code: -1
Call: INSERT INTO TEST (ID, NAME) VALUES (?, ?)
幕后发生了什么?问题是否与Test的appearOnTestRuns属性有关?为什么JPA不理解Test已经存在于DB中(毕竟,在find操作期间设置了id属性,equals()使用该值来决定2个测试是否相等..) 非常感谢你的帮助! :)
PS:你知道一本很好的教程/书籍,这些概念被广泛解释了吗?
答案 0 :(得分:0)
首先,您应该检查整个createTestRun
方法(找到测试,在必要时创建测试,然后创建TestRun
实例并保留它)在单个事务中运行。
你的newTestRun方法应该初始化关系的两边,这是双向的。开发人员有责任维护实体图的一致性。因此应该这样做:
public static TestRun newTestRun(Test test){
TestRun run = new TestRun();
run.setPerformedTest(test);
test.getAppearsOnTestRuns().add(run);
return run;
}