hibernate 同一テーブルの多対多構造

さて、mixiマイミクのような、
同一テーブルの多対多構造を持つ場合、
hibernateを使うと何かと問題が出やすい。
特に、insert、delete。

これがスタンダードなやり方かわからないが、
一応うまくいっているので、紹介する。

まず、DBの構造。
ユーザは割愛するとして、
リンクテーブルは
parent_user_id
friend_usr_id
の2つがprimary keyになっているとしましょう。

そんでもって、
many-to-oneで定義済みってことで。

まず、追加。
ユーザAとBをマイミクにする場合、
A.getFriends()でマイミクさんを取れることにしましょう。
その場合、
単純に

A.getFriends().add(B);
B.getFriends().add(A);
とすれば良いような気がしますが、
これが失敗します。
入れ子構造になっているため、2行目でBが追加されたAが入るため、
insert文が2つ流れてしまい、重複キーエラーが出るんですね。

これを解消するためには、
session.evict(B);
A.getFriends().add(B);
B = session.load(); <=読み直し
session.evict(A);
B.getFriends().add(A);
とevict()を駆使する必要があります。

同様に、削除も
A.getFriends().remove(B);
B.getFriends().remove(A);
ではダメで、
session.evict(B);
A.getFriends().remove(B);
B = session.load(); <=読み直し
session.evict(A);
B.getFriends().remove(A);
とする必要があります。

実際のところ、この設計がまずいのかもしれませんがね。