2011-07-05 7 views
3

私はJPAを初めて使用していますが、カスケードや関連の理解が不十分なため、次の問題が発生していますか?エンティティの更新時にjava.sql.SQLIntegrityConstraintViolationExceptionが発生する

@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 
    ... 
    } 

関係は、私は、それぞれ複数回施行しているテスト-Sのセットを持っていることを意味します

@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 + " ]"; 
    } 
} 

そして:ManyToOne協会 - 私は双方向OneToManyにある2つのエンティティを持っています時間はTestRunに関連付けられています。 はTestRunを作成するために、私は次のメソッドを使用します。新しいテスト(挿入)とcreateTestRunお得な情報は、テストオブジェクトが正しく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; 
    } 
} 

。しかし、テストがすでに存在する場合、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はDBにテストが既に存在しているのか理解していません(結局findオペレーション中にidプロパティが設定され、equals()はこの値を使って2つのテストが等しいかどうかを判断します)。 ありがとう! :)

PS:あなたはこれらの概念が広範に説明されている良いチュートリアル/本を知っていますか?

+0

私たちはEntityFactory.newTestRun(testPerformed)のコードを教えてください。 –

+0

こんにちは!私はちょうどそれをやった! :)私は思った:DBから取得したTest-sはejb.find()が返った後にデタッチされるので、同じトランザクション内から手動でマージして新しいTestRunオブジェクトを永続化する必要がありますか? – Federico

+0

これらは取り外さないでください。 createTestRunメソッド全体を単一のトランザクションで実行する必要があります。アトミックなクエリやデータベースへの更新ではなく、アトミックな機能の変更にトランザクションを使用する必要があります。 –

答えて

0

最初に、createTestRunメソッド(テストを見つけ出し、必要に応じて作成してからTestRunインスタンスを作成してそれを維持する)が1回のトランザクション内で実行されることを確認する必要があります。

newTestRunメソッドは、双方向の関係の両端を初期化する必要があります。エンティティグラフの一貫性を維持するのは開発者の責任です。このようにしてください:

public static TestRun newTestRun(Test test){ 
    TestRun run = new TestRun(); 
    run.setPerformedTest(test); 
    test.getAppearsOnTestRuns().add(run); 
    return run; 
} 
関連する問題