8

私の質問はこれとかなり似ていますHibernate Bi-Directional ManyToMany Updates with Second Level cache

私は以下に示すようにクラスを持っています

@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Entity 
public class A{
     private int id;
     private List<B> listB;

     ...
     @Cache (usage = CacheConcurrencyStrategy.TRANSACTIONAL)
     @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity = B.class)
     @JoinTable(name = "A_B", joinColumns = { @JoinColumn(name = "a_id") }, 
        inverseJoinColumns = { @JoinColumn(name = "b_id") })
     public List<B> getListB() {
         return listB ;
     }
}

@Cache (usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Entity
public class B{
     private int id;
     private List<A> listA;

     @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity = A.class)
     @JoinTable(name = "A_B", joinColumns = { @JoinColumn(name = "b_id") }, inverseJoinColumns = { @JoinColumn(name = "a_id") })
     public List<a> getListA() {
         return listA ;
     }

     public void addToA(A a) {
         a.getListB().add(this);
     }
}

多対多の関係で予想されるように、双方向の関係の両側を更新してきました。

今私が直面している問題は、コレクション内のアイテムを追加/更新しようとすると、エントリが重複してポップアップすることです。以下は、エンティティを永続化するために使用するコードです...

b.getA().clear() ;
...
...
b.getListA().add(A) ;
b.addToA(A) ;
em.saveOrUpdate(b) ;

以下は、ログから取得された Hibernate によって起動されるクエリです。

delete from A_B where b_id=?

insert into A_B (b_id, a_id) values (?, ?)
insert into A_B (b_id, a_id) values (?, ?)

delete from A_B where a_id=?

insert into A_B (a_id, b_id) values (?, ?)
insert into A_B (a_id, b_id) values (?, ?)
insert into A_B (a_id, b_id) values (?, ?)
insert into A_B (a_id, b_id) values (?, ?)

ここでどこが間違っていますか?挿入されている重複を取り除く方法は? キャッシュは適切にフラッシュされていますが、エントリの重複が唯一の問題です。

4

1 に答える 1

15

これはクラシックです!

問題は、一方が所有者であり、もう一方が逆である場合に、両方のマッピングが所有者であるということです。どちらも所有者であるため、どちらかを変更するとデータベースが挿入されます。1つの所有者と1つの逆の場合、挿入のセットは1つだけになります。

次のように書き直すことができるはずですB::getListA

 @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "listB")
 public List<A> getListA()

そして、すべてが機能します。

注釈として所有側のみに注意してください@JoinTable。原則として、データベースの任意のビットは、JPAアプリケーションの正確に1つの場所にマップされます。何かを2回マッピングしていることに気付いた場合は、それを行うためのより良い方法があるかどうかをじっくりと調べてください。

targetEntityちなみに、属性は必要ありません。JPAプロバイダーは、ジェネリックからそれを解決できます。

于 2012-11-05T08:37:01.947 に答える