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


114

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()保存する前にも試してみましたが、まだうまくいきませんでした。

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

助言がありますか?


回答:


173

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

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

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

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

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

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

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

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


4
いい答え²。主キーは私の問題でしたが、GeneredValueを設定してpostgresqlのシーケンスを解決しました。
ロドリゴフェラーリ

2
同じ問題がありました。私の場合、コードでオブジェクトを検索し、最初のオブジェクトがまだ休止状態のセッションにある間に、他のコードで同じIDの新しいオブジェクトを作成しようとしました。
dellasavia 14

18

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


13
私も休止状態が嫌いです...そしてデータベース...それらが私に引き起こしたすべての問題の後で、私はテキストファイルを使用する方が簡単だと思います(冗談ですが、それでも...)。
イゴールポポフ2011年

私の場合、それらは新しくて1つを持っていないため、同じ識別子0を持つ多くのオブジェクトがあります。dbがそれらを生成します。どこかで、0信号が設定されていないことを読みました。それらを永続化する直感的な方法は、それらを繰り返し、オブジェクトを保存するために休止状態と言うことです。私はこれを正確に行う必要があります。これを行う「休止状態の方法」はどれか教えていただけませんか?
ラムセス2014

私の場合、このオブジェクトのインスタンスが2つあるので、session.merge(myobject)を使用する必要がありました。このエンティティの別のインスタンスが休止状態になっていることが要求されています。この2番目のインスタンスは、セッションに接続されたままでした。最初のインスタンスが変更されます。getj2ee.over-blog.com/…の
Reddymails

問題を起こすのは休止状態ではありませんが、それがどのように機能するかを理解していません
ACV

17

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


11

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


9

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



5

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


4

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

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

オブジェクトは、別の休止状態セッションで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

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

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

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

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

4

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

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

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


3

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

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

3
良い試みですが、オブジェクト「ob」は更新されたデータなしで返されます(データベースから取得したオブジェクトとその保存の間で、実行時にアプリケーションを通じて更新されたと見なされます)。
アレックス

2

Idマッピングは正しいですか?データベースが識別子を介してIDを作成する責任がある場合は、ユーザーオブジェクトをそれにマッピングする必要があります。


2

私はオブジェクトを削除することでこの問題に遭遇しました。

/**
 * 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

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

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

したがって、このように1つのセッションで、同じ識別子を持つエンティティは1つだけです。


2

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

を使用てレコードを選択し同じセッションを使用して同じ識別子を持つ別のレコードをgetsession() 再度更新すると、この問題が発生します。以下のコードを追加しました。

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

これは絶対に行わないでください。解決策は、ビジネスロジックを更新または変更する前にセッションを削除することです。


1

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


私の質問にあなたの解決策をどのように適用できますか?クラスにid列を作成するにはどうすればよいですか?stackoverflow.com/questions/55732955/…–
sbattou

1

IDがnullか0のどちらかを取るかどうかを確認するだけです

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

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


1

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

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


1

@GeneratedValue(strategy = GenerationType.IDENTITY)の場合、このアノテーションをエンティティBeanの主キープロパティに追加すると、この問題が解決するはずです。


1

この問題を解決しました。
実際、これはBeanクラスのジェネレータタイプのPKプロパティの実装を忘れたために発生しています。したがって、次のような任意のタイプにします

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

Beanのオブジェクトを永続化すると、すべてのオブジェクトが同じIDを取得するため、最初のオブジェクトが保存されます。別のオブジェクトを永続Exception: org.hibernate.NonUniqueObjectException:化すると、同じ識別子の値を持つこのタイプの異なるオブジェクトを介したHIB FW がすでにセッションに関連付けられていました。


1

この問題は、同じ休止状態のセッションで、同じ識別子を持つ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

同様の問題がありました。私の場合は、設定し忘れていた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

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

私が助けてくれたことを願っています;-)


1

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

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

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

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


1

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

ではhbm.xml、ファイル

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

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

    <generator class="identity">

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


0

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


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

ではごきげんよう


0

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

あなた自身のイコールとハッシュコードを実装することもあなたを助けるかもしれません。


0

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


0

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

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



0

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

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.