web-dev-qa-db-ja.com

Hibernate Error:org.hibernate.NonUniqueObjectException:同じ識別子の値を持つ別のオブジェクトはすでにセッションに関連付けられていました

2つのユーザーオブジェクトがあり、使用してオブジェクトを保存しようとしています

session.save(userObj);

次のエラーが表示されます。

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#com.pojo.rtrequests.User@d079b40b]

私はを使用してセッションを作成しています

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

保存する前にsession.clear()を試してみましたが、まだ運がありません。

これは、最初にユーザーリクエストが来たときにセッションオブジェクトを取得しているので、オブジェクトがセッションに存在していると言っている理由がわかります。

助言がありますか?

102
harshit

私はこのエラーを何度も経験しましたが、追跡するのは非常に困難です...

基本的に、Hibernateが言っているのは、同じ識別子(同じ主キー)を持つ2つのオブジェクトがありますが、それらは同じオブジェクトではないということです。

コードを分解することをお勧めします。つまり、エラーがなくなるまでビットをコメントアウトしてから、コードが戻ってエラーが見つかるまでコードを戻します。

ほとんどの場合、オブジェクトAとBの間にカスケード保存があるカスケード保存を介して発生しますが、オブジェクトBはすでにセッションに関連付けられていますが、Bの同じインスタンスには関連付けられていません。

どの主キージェネレーターを使用していますか?

私が尋ねる理由は、このエラーは、オブジェクトの永続的な状態を確認するように休止状態に指示する方法に関連していることです(つまり、オブジェクトが永続的かどうか)。このエラーは、Hibernateが永続化を試みており、オブジェクトが既に永続化されているために発生している可能性があります。実際、saveを使用する場合、Hibernateはそのオブジェクトを保持しようとしますが、セッションに関連付けられた同じ主キーを持つオブジェクトが既に存在する可能性があります。

主キーの組み合わせ(列1と列2)に基づいた10行のテーブルのhibernateクラスオブジェクトがあると仮定します。これで、ある時点でテーブルから5つの行が削除されました。ここで、同じ10行を再度追加しようとすると、hibernateがデータベース内のオブジェクトを永続化しようとしているときに、すでに削除された5行がエラーなしで追加されます。すでに存在する残りの5行は、この例外をスローします。

したがって、簡単なアプローチは、何かの一部であるテーブルの値を更新/削除したかどうかをチェックし、後で同じオブジェクトをもう一度挿入しようとしていることです

164
Michael Wiles

これは、休止状態が解決するよりも多くの問題を引き起こす1つのポイントにすぎません。私の場合、同じ識別子0を持つオブジェクトが多数あります。それらは新しいものであり、1つもないためです。データベースはそれらを生成します。どこかで、シグナル0が設定されていないことを読んでいます。それらを永続化する直観的な方法は、それらを繰り返し、オブジェクトを保存するために休止状態を言うことです。しかし、あなたはそれをすることはできません-「もちろん、休止状態がこれと同じように機能することを知っておく必要があります。したがって、あなたはする必要があります。」休止状態は単なる追加の不透明な負担であるため、最終的には独自の単純なマッパーを使用して簡単に実行できます。別の例:1つのデータベースからパラメーターを読み取って別のデータベースに保持しようとすると、ほぼすべての作業を手動で行う必要があります。しかし、とにかくそれをしなければならない場合、休止状態を使用することは単なる追加作業です。

18
Hein

USe session.evict(object);evict()メソッドの関数は、セッションキャッシュからインスタンスを削除するために使用されます。したがって、オブジェクトを初めて保存するには、キャッシュからオブジェクトを削除する前にsession.save(object)メソッドを呼び出してオブジェクトを保存します。同様に、evict()を呼び出す前にsession.saveOrUpdate(object)またはsession.update(object)を呼び出してオブジェクトを更新します。

14
Rahul N

これは、読み取りと書き込みに同じセッションオブジェクトを使用した場合に発生する可能性があります。どうやって? 1つのセッションを作成したとします。主キーEmp_id = 101を使用して従業員表からレコードを読み取りました。Javaでレコードを変更しました。そして、従業員レコードをデータベースに保存します。ここではセッションを閉じていません。読み取られたオブジェクトもセッションに保持されるため。書き込みたいオブジェクトと競合します。したがって、このエラーが発生します。

9
Prashant Bari

誰かがすでに上で指摘したように、cascade=all関係の両端にone-to-manyがあったときにこの問題に遭遇したので、A-> B(Aから1対多、そして多対多-Bの1つで、AのBのインスタンスを更新してからsaveOrUpdate(A)を呼び出すと、循環保存要求、つまりAの保存がAの保存をトリガーするBの保存をトリガーし、3番目のインスタンスでエンティティ(A)をsessionPersistenceContextに追加しようとしたため、duplicateObject例外がスローされました。
一端からカスケードを削除することで解決できました。

8
redzedi

私もこの問題に遭遇し、エラーを見つけるのに苦労しました。

私が抱えていた問題は次のとおりでした:

オブジェクトは、異なる休止状態のセッションでDaoによって読み取られました。

この例外を回避するには、後でこのオブジェクトを保存/更新するdaoでオブジェクトを再読み込みします。

そう:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

一部の人々が多くの時間を節約できることを願っています!

4
Frithjof

私はこの問題に遭遇しました:

  1. オブジェクトの削除(HQLを使用)
  2. 同じIDの新しいオブジェクトをすぐに保存する

削除後に結果をフラッシュし、新しいオブジェクトを保存する前にキャッシュをクリアすることで解決しました

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();
4
wbdarby

同じ識別子永続オブジェクトを使用して異なるセッションで保存する場合は、session.merge(obj)を使用できます。
それは機能しました。以前にも同じ問題がありました。

3
Pavan Kumar

セッション内のオブジェクトを取得します。ここに例を示します。

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);
3
sock_osg

オブジェクトの削除でこの問題が発生しましたが、追い出しも明確な助けもありませんでした。

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}
2
TimP

IDマッピングは正しいですか?データベースがIDを使用してIDを作成する場合、ユーザーオブジェクトをその..にマップする必要があります。

2
cwap

ジャクソンから入手した新しいセットとセットを交換したときと同じエラーがあります。

これを解決するために、既存のセットを保持し、retainAllを使用して新しいリストに不明な要素を古いセットから削除します。次に、addAllで新しいものを追加します。

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

セッションを持って操作する必要はありません。

1
Ôrel

この問題は、同じHibernateセッションで同じ識別子を持つ2つのオブジェクトを保存しようとしているために発生します。2つの解決策があります。

  1. これは、以下のようにidフィールドに対してmapping.xmlファイルを正しく設定していないために発生しています。

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
    
  2. GetsessionメソッドをオーバーロードしてisSessionClearのようなパラメーターを受け入れ、以下のように現在のセッションを返す前にセッションをクリアします

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }
    

これにより、既存のセッションオブジェクトがクリアされ、Hibernateが一意の識別子を生成しない場合でも、Auto_Incrementなどを使用してプライマリキー用にデータベースを適切に構成していると仮定すると、動作します。

1
Rahul Kumar

nullまたは0のようにIDを確認するだけです

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

コンテンツがフォームからPojoに設定される場所を追加または更新する

1
Ayan Sarkar

@GeneratedValue(strategy = GenerationType.IDENTITY)、エンティティBeanの主キープロパティにこの注釈を追加すると、この問題を解決できます。

1
Jay

この問題は、データベースからオブジェクトを取得するために使用したセッションの同じオブジェクトを更新するときに発生します。

Updateメソッドの代わりにhibernateのmergeメソッドを使用できます。

例えば最初にsession.get()を使用してから、session.merge(オブジェクト)を使用できます。この方法では問題は発生しません。 merge()メソッドを使用して、データベース内のオブジェクトを更新することもできます。

1

wbdarbyが言ったこと以外に、オブジェクトの識別子をHQLに与えることでオブジェクトがフェッチされたときにも起こり得る。オブジェクトフィールドを変更してDBに保存しようとすると(変更は挿入、削除、または更新)同じセッションでの場合、このエラーが表示されます。変更したオブジェクトを保存する前に休止状態セッションをクリアするか、新しいセッションを作成してください。

助けてくれた;-)

1

これを試して。以下は私のために働いた!

hbm.xmlファイル内

  1. クラスタグのdynamic-update属性をtrueに設定する必要があります。

    <class dynamic-update="true">
    
  2. 一意の列の下のジェネレータータグのクラス属性をidentityに設定します。

    <generator class="identity">
    

注:一意の列をidentityではなくassignedに設定します。

1
Explorer_N

@Id列に@GenerateValueを入れるのを忘れたかどうかを確認します。映画とジャンルの多対多の関係でも同じ問題がありました。プログラムはHibernateエラーをスローしました:org.hibernate.NonUniqueObjectException:同じ識別子の値を持つ別のオブジェクトはすでにセッションエラーに関連付けられていました。後で、GenreId getメソッドに@GenerateValueを指定するだけでよいことがわかりました。

1
Thupten

私はNHibernateを初めて使用しますが、問題は、オブジェクトを保存するのとは異なるセッションを使用してオブジェクトを照会したことです。そのため、保存セッションはオブジェクトについて知りませんでした。

当たり前のように思えますが、以前の回答を読んで、2つのセッションではなく2つのオブジェクトを探していました。

1
DustinA

私はこの問題を解決しました。
実際、これはBeanクラスのGenerator Type of PKプロパティの実装を忘れたために発生しています。のような任意のタイプにします

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

beanのオブジェクトを永続化すると、すべてのオブジェクトが同じIDを取得するため、最初のオブジェクトが保存されます。別のオブジェクトを永続化する場合、このタイプのException: org.hibernate.NonUniqueObjectException:を介してHIB FWセッション。

1
Ankit Sharma

繰り返しオブジェクトが始まる位置の前に、セッションを閉じてから、新しいセッションを開始する必要があります

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

したがって、この方法では、1つのセッションで同じ識別子を持つエンティティは複数存在しません。

1
engtuncay

同様の問題がありました。私の場合、データベースのincrement_by値をcache_sizeallocationSizeで使用される値と同じ値に設定するのを忘れていました。 (矢印は上記の属性を指します)

SQL:

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

Java:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)
1
kaba713

カスケード設定を確認できます。モデルのカスケード設定が原因である可能性があります。カスケード設定を削除し(本質的にカスケード挿入/更新を許可しない)、これで問題が解決しました

0
user1609848

セッションフラッシュはいつでも実行できます。フラッシュは、セッション内のすべてのオブジェクトの状態を同期します(私が間違っている場合は誰かが私を修正してください)。

独自のequalsとハッシュコードを実装することも役立ちます。

0
caarlos0

私はそのような同様の問題を解決しました:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
0
user3132194

これは役立ちますか?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 

私のために働いたもう1つのことは、インスタンス変数をlongの代わりにLongにすることでした


主キー変数の長いIDがありました。 Long idに変更します。働いた

ではごきげんよう

0
Ashishakgupta

パーティーに遅れましたが、今後のユーザーには役立つかもしれません-

この問題は、i selectgetsession()を使用するレコードと、再度pdate同じidentifierを使用する別のレコード同じsessionが問題の原因です。以下のコードを追加しました。

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

これは決して行われるべきではありません。解決策は、ビジネスロジックを更新または変更する前にセッションを削除することです。

0
vipin cp

この問題を解決する1つの回避策は、更新を行ってから永続化する前に、Hibernate cache/dbからオブジェクトを読み戻そうとすることです。

例:

            OrderHeader oh = orderHeaderDAO.get(orderHeaderId);
            oh.setShipFrom(facilityForOrder);
            orderHeaderDAO.persist(oh);

注:これは根本原因を修正するのではなく、問題を解決することに注意してください。

0
Nuwan

私もこのエラーを見つけました。私のために働いたのは、主キー(つまり自動生成される)がPDT(つまりlong、int、ect。)ではなく、オブジェクト(つまりLong、Integerなど)であることを確認することです

オブジェクトを作成して保存するときは、0ではなくnullを渡すようにしてください。

0