更新实体时的java.sql.SQLIntegrityConstraintViolationException

时间:2011-07-05 11:48:09

标签: java jpa

我是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:你知道一本很好的教程/书籍,这些概念被广泛解释了吗?

1 个答案:

答案 0 :(得分:0)

首先,您应该检查整个createTestRun方法(找到测试,在必要时创建测试,然后创建TestRun实例并保留它)在单个事务中运行。

你的newTestRun方法应该初始化关系的两边,这是双向的。开发人员有责任维护实体图的一致性。因此应该这样做:

public static TestRun newTestRun(Test test){
    TestRun run = new TestRun();
    run.setPerformedTest(test);
    test.getAppearsOnTestRuns().add(run);
    return run;
}