可能性のある複製:
Hibernate Error:org.hibernate.NonUniqueObjectException:同じ識別子値を持つ別のオブジェクトはすでにセッションに関連付けられていました
dAO.update(userbean)を使用すると、session.SaveOrUpdate(e);
throw例外:同じ識別子の値を持つ異なるオブジェクトがすでにセッションに関連付けられていました
関数は次のようなものです:
public E save(E e) {
Session session = null;
try {
session = sessionFactory.openSession();
log.debug("session="+session.hashCode()+" save "+e);
session.SaveOrUpdate(e); //here throws exception
session.flush();
}
catch (Exception e1) {
log.err("Cannot open hibernate session "+ e1.getMessage()+" cause : "+e1.getCause());
e1.printStackTrace();
}
finally { if ( session != null ) session.close(); session = null;}
return e ;
}
userbeanはUserBeanクラスのインスタンスです
public class UserBean{
private List<GroupBean> groups = new ArrayList<GroupBean> ();
private List<RoleBean> roles = new ArrayList<RoleBean> ();
}
public class GroupBean{
private List<RoleBean> roles = new ArrayList<RoleBean> ();
}
すべてのgroupbeanにはロールのリストがあり、それらは変更されません。
データベースでは、グループとロールは多対多マッピングです。
例えば、
groupbean#1、ロールです:rolebean#1、rolebean#2;
groupbean#2、rolerole#1。
今、新しいuserbean#1を作成し、そのグループはgroupbean#1です。userbean#1にrolebean#1を追加したい場合、タイトル記述子のような例外をスローします
Server.logを見ると、DAO.saveを使用すると、saveOrUpdateの順序は次のようになります。
userbean#1
|---|-----------***userbean.groups
| | groupbean#1
| | groupbean.roles
| | rolebean#1 # save relebean#1 the first time
| | ---done rolebean#1
| | ------done all rolebeans of group.roles
| | ---done groupbean#1
| |-----------done all groupbeans of userbean.groups
|---|-----------***userbean.roles
| rolebean#1 # save rolebean#1 the second time, and throws exception here!
| ----done rolebean#1
| .....
|-----------done all rolebeans of userbean.roles
例外の原因は、rolebean#1がセッションで2回保存され、それらのIDが同じであることにあります。
関数save(E e)で、使用する場合
session.merge(e);
取り替える
session.SaveOrUpdate(e);
例外をスローしませんが、rolebean#1はuserbean#1に関連付けられていません
誰でもこれについていくつかの提案をすることができますか?
ユーザーとグループの両方にロールBeanを割り当てているコードを確認できれば、正確な原因を特定するのが簡単になります。
一般的に、例外からわかるのは、そのロールBeanには2つのバージョン(2つのインスタンス)があるということです。最初のものが更新され、Hibernateが2番目のものをヒットし、それが同じ識別子であるがロールの異なる分離バージョンであることを認識します。
Hibernateはどちらが正しいかわからないため、saveOrUpdateの下で例外をスローして通知します。
Mergeのコントラクトは異なる動作をします。つまり、再度保存する(つまり、すべての変更をマージする)ことを想定しているため、2番目のバージョンを再アタッチし、すべての変更をマージして、更新を保存します。
SaveOrUpdate vs Merge についてブログに書いて、何が起こっているのかを説明するためにもう少し詳しく説明しました。
SaveOrUpdateを使用したい場合は、ユーザーの役割コレクションとグループではなく、役割の異なるインスタンスが割り当てられる原因となっている割り当てで何をしているのかを把握する必要があります。
それ以外の場合、マージの効果が機能する場合(JPA標準に準拠)、それを使用します。