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


91

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

  • 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に設定された空白のオブジェクトとしてデータベースに挿入されます。明らかにそれは私たちが望んでいることではありません。

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


この回答が役立つかどうかを確認してください..
joaonlima 2013

回答:


97

Bオブジェクトが同じJava Cオブジェクトインスタンスを参照していないことが原因と考えられます。それらはデータベース内の同じ行(つまり、同じ主キー)を参照していますが、それらの異なるコピーです。

つまり、エンティティを管理しているHibernateセッションが、同じJavaキーを持つ行に対応するJavaオブジェクトを追跡しているということです。

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


3
ありがとうjbx。おっしゃったように、Bオブジェクトはメモリ内の複数のCインスタンスを参照していることがわかります。本質的に何が起こっているかというと、私のプログラムの一部がCを読み取ってBにアタッチしていることです。別の部分は、データベースから同じCの異なるBをロードしています。どちらもAにアタッチされているため、保存時にエラーが発生します。B-> C関係の<pre> cascade </ pre>を "<pre> none </ pre>"に設定しましたが、それでも同じエラーが発生します。多対1または1対多の関係で、Hibernateに外部キーを変更するだけで残りを心配しないように指示する方法はありますか?
ジョン

1
Cの主キーにはID生成戦略がありますか?シーケンスジェネレーターか似たようなものですか?
jbx 2013

はい、それぞれデータベースに独自のシーケンスがあります。あなたが言ったように、カスケードが問題であることがわかりました。タイプテーブルのカスケードをオフにし、その他の場合は「マージ」カスケードを使用しました。これにより、すべてのnull行を作成せずにmerge()を呼び出すことができました。私はそれに応じてあなたの答えをマークしました、ありがとう!
ジョン

14
saveOrUpdate()とBOOMの代わりにmerge()を使用しました!動作します:)
Lahiru Ruhunage 2013年


13

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


9

@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後で、主キーの自動増加を忘れていることがわかりました

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

@GeneratedValue(strategy = GenerationType.AUTO)

6

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

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

@Id
private Integer id;

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

1
説明どおりの質問の場合はそうではありません
Sudip Bhandari

5

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

<generator class="native"/>

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



3

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

getHibernateTemplate().flush();

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


2

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

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

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

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


2

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


1

私はこのエラーを数日試しましたが、このエラーの修正に時間をかけすぎました。

 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つの例がどのように発生するかを示します。


1

前にクエリのコードを配置してください。それで問題が解決しました。例えばこれを変更してください:

query1 
query2 - get the error 
update

これに:

query2
query1
update

0

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


3
もしそうでなければ、彼はこの問題を抱えていなかっただろう。問題は、彼が同じ識別子を持つ2つのオブジェクトを持っていることです。
aalku 2013

0

次のように行を挿入すると、主キーの生成が間違っているために問題が発生しました。

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

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


0

確認してください、エンティティはすべてのマッピングされたエンティティと同じ世代タイプです

例:UserRole

public class UserRole extends AbstractDomain {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

@Enumerated(EnumType.STRING)
private CommonStatus status;

private String roleCode;

private Long level;

@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;

private String modification;

@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

モジュール:

public class Modules implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

}

マッピングのある主要エンティティ

public class RoleModules implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;

@Type(type = "yes_no")
private boolean isPrimaryModule;

public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}


0

以前のすべての回答に加えて、クラスに値オブジェクトを使用してVO Transformerクラスのid属性を設定しない場合、大規模プロジェクトでこの問題を解決できる可能性があります。


0

現在のトランザクションをコミットするだけです。

currentSession.getTransaction().commit();

これで、別のトランザクションを開始して、エンティティで何かを行うことができます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.