web-dev-qa-db-ja.com

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

基本的に、この構成にはいくつかのオブジェクトがあります(実際のデータモデルはもう少し複雑です)。

  • AはBと多対多の関係にあります(Bにはinverse="true"があります)
  • BはCと多対1の関係にあります(cascade"save-update"に設定しています)
  • Cは、一種のタイプ/カテゴリテーブルです。

また、主キーは保存時にデータベースによって生成されることをおそらく言及する必要があります。

私のデータでは、Aが一連の異なるBオブジェクトを持ち、これらのBオブジェクトが同じCオブジェクトを参照するという問題に遭遇することがあります。

session.saveOrUpdate(myAObject)を呼び出すと、"a different object with the same identifier value was already associated with the session: C"という休止エラーが発生します。 hibernateは同じセッションで同じオブジェクトを2回挿入/更新/削除できないことを知っていますが、これを回避する方法はありますか?これは、状況の珍しいことではないようです。

この問題の調査中に、人々がsession.merge()の使用を提案するのを見てきましたが、それを行うと、すべての値がnullに設定された空白オブジェクトとして「競合する」オブジェクトがデータベースに挿入されます。明らかにそれは私たちが望むものではありません。

[編集]私が言及するのを忘れたもう一つのことは、(私の制御を超えたアーキテクチャ上の理由から)、各読み取りまたは書き込みは別々のセッションで行われる必要があるということです。

71
John

最も可能性が高いのは、Bオブジェクトが同じJava Cオブジェクトインスタンスを参照していないためです。それらはデータベース内の同じ行(つまり、同じ主キー)を参照していますが、それらは異なるコピーです。

そのため、エンティティを管理しているHibernateセッションは、どのJavaオブジェクトが同じ主キーを持つ行に対応しているかを追跡していることになります。

1つのオプションは、同じ行を参照するオブジェクトBのエンティティが実際にCの同じオブジェクトインスタンスを参照していることを確認することです。あるいは、そのメンバー変数のカスケードをオフにします。この方法は、Bが永続化されるときCはそうではありません。ただし、Cを個別に手動で保存する必要があります。 Cが型/カテゴリテーブルである場合、おそらくそのようにするのが理にかなっています。

75
jbx

カスケードをMERGEに設定するだけで、うまくいくはずです。

21
4n0r23j

必要なことは1つだけです。 session_object.clear()を実行してから、新しいオブジェクトを保存します。これにより、適切な名前のセッションがクリアされ、問題の重複オブジェクトがセッションから削除されます。

10
dsk

@Hemant kumarに同意します。彼の解決策に従って、私は私の問題を解決しました。

例えば:

@Test
public void testSavePerson() {
    try (Session session = sessionFactory.openSession()) {
        Transaction tx = session.beginTransaction();
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("222");
        person2.setName("111");
        session.save(person1);
        session.save(person2);
        tx.commit();
    }
}

Person.Java

public class Person {
    private int id;
    private String name;

    @Id
    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

このコードは私のアプリケーションで常に間違いを犯します:A different object with the same identifier value was already associated with the session、later、私は自動で主キーを増やす!

私の解決策は、主キーにこのコードを追加することです:

@GeneratedValue(strategy = GenerationType.AUTO)
6
Ice Blue

以下を使用して、オブジェクトIDをHibernateからデータベースに割り当てるタスクを転送します。

<generator class="native"/>

これで問題は解決しました。

5
user2845946

挿入するBeanにアノテーション@GeneratedValueを追加します。

3
Hemant kumar

上記の問題を解決する1つの方法は、hashcode()をオーバーライドすることです。
また、保存の前後に休止状態セッションをフラッシュします。

getHibernateTemplate().flush();

デタッチされたオブジェクトを明示的にnullに設定することも役立ちます。

3
Waqar

これは、同じオブジェクトへの参照を使用して、テーブル内の複数の行を保存しようとしていることを意味します。

エンティティクラスのidプロパティを確認してください。

@Id
private Integer id;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
3
rex roy

Hibernateで「Cascade」属性を見つけて削除します。 「カスケード」を使用可能に設定すると、関連するクラスと関係がある別のエンティティで他の操作(保存、更新、削除)が呼び出されます。したがって、同じID値が発生します。それは私と一緒に働いた。

2
Nguyen Vu Quang

このメッセージに出会ったのはC#コードです。関連するかどうかはわかりません(ただし、まったく同じエラーメッセージです)。

ブレークポイントを使用してコードをデバッグし、デバッガーがブレークポイントにある間にプライベートメンバーを介していくつかのコレクションを展開しました。構造を掘り下げずにコードを再実行すると、エラーメッセージが消えました。プライベートレイジーロードされたコレクションを調べる行為により、NHibernateはその時点でロードされるはずのないものをロードしたようです(プライベートメンバーであるため)。

コード自体はかなり複雑なトランザクションにラップされており、そのトランザクションの一部として多数のレコードと多くの依存関係を更新できます(インポートプロセス)。

うまくいけば、問題に出くわした他の誰かへの手がかり。

数日このエラーが発生し、このエラーの修正に時間がかかりすぎました。

 public boolean save(OrderHeader header) {
    Session session = sessionFactory.openSession();


    Transaction transaction = session.beginTransaction();

    try {
        session.save(header);

        for (OrderDetail detail : header.getDetails()) {
            session.save(detail);
        }

        transaction.commit();
        session.close();

        return true;
    } catch (HibernateException exception) {

        exception.printStackTrace();
        transaction.rollback();
        return false;
    }
}

このエラーが発生する前に、OrderDetilオブジェクトでID生成タイプについて言及していませんでした。 OrderdetailsのIDを生成せずに、すべてのOrderDetailオブジェクトのIDを0のままにします。これは#jbxが説明したものです。はい、それは最良の答えです。これがどのように起こるかの一例。

1
Buddhi

前にクエリのコードを配置してみてください。それは私の問題を修正します。例えばこれを変更します:

query1 
query2 - get the error 
update

これに:

query2
query1
update
1
juldeh

entityRepositoryを使用する場合、saveの代わりにsaveAndFlushを使用します

0

IDEの式タブを開いたままにしておくと、この例外の原因となるオブジェクトに対して休止状態のget呼び出しが行われていました。この同じオブジェクトを削除しようとしていました。また、このエラーを発生させるために必要と思われる削除呼び出しにブレークポイントがありました。別の式タブをフロントタブにするか、IDEがブレークポイントで停止しないように設定を変更するだけで、この問題は解決しました。

0
Aaron Byrnes

このような行を挿入すると、主キーの生成が間違っているために問題に遭遇しました:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
        // TODO Auto-generated method stub
        try {
            Set<Byte> keySet = map.keySet();
            for (Byte byte1 : keySet) {
                Device device=new Device();
                device.setNumDevice(DeviceCount.map.get(byte1));
                device.setTimestamp(System.currentTimeMillis());
                device.setTypeDevice(byte1);
                this.getHibernateTemplate().save(device);
            }
            System.out.println("hah");
        }catch (Exception e) {
            // TODO: handle exception
            logger.warn("wrong");
            logger.warn(e.getStackTrace()+e.getMessage());
        }
}

Idジェネレータークラスをidentityに変更します

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>
0
张云风

私の場合、flush()のみが機能しませんでした。 flush()の後にclear()を使用する必要がありました。

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}
0
shubhranshu

更新クエリを呼び出す前にオブジェクトの識別子を設定していない可能性があります。

0
Fawad Khaliq