基本的に、この構成にはいくつかのオブジェクトがあります(実際のデータモデルはもう少し複雑です)。
inverse="true"
があります)cascade
を"save-update"
に設定しています)また、主キーは保存時にデータベースによって生成されることをおそらく言及する必要があります。
私のデータでは、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に設定された空白オブジェクトとして「競合する」オブジェクトがデータベースに挿入されます。明らかにそれは私たちが望むものではありません。
[編集]私が言及するのを忘れたもう一つのことは、(私の制御を超えたアーキテクチャ上の理由から)、各読み取りまたは書き込みは別々のセッションで行われる必要があるということです。
最も可能性が高いのは、Bオブジェクトが同じJava Cオブジェクトインスタンスを参照していないためです。それらはデータベース内の同じ行(つまり、同じ主キー)を参照していますが、それらは異なるコピーです。
そのため、エンティティを管理しているHibernateセッションは、どのJavaオブジェクトが同じ主キーを持つ行に対応しているかを追跡していることになります。
1つのオプションは、同じ行を参照するオブジェクトBのエンティティが実際にCの同じオブジェクトインスタンスを参照していることを確認することです。あるいは、そのメンバー変数のカスケードをオフにします。この方法は、Bが永続化されるときCはそうではありません。ただし、Cを個別に手動で保存する必要があります。 Cが型/カテゴリテーブルである場合、おそらくそのようにするのが理にかなっています。
カスケードをMERGEに設定するだけで、うまくいくはずです。
必要なことは1つだけです。 session_object.clear()
を実行してから、新しいオブジェクトを保存します。これにより、適切な名前のセッションがクリアされ、問題の重複オブジェクトがセッションから削除されます。
@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)
以下を使用して、オブジェクトIDをHibernateからデータベースに割り当てるタスクを転送します。
<generator class="native"/>
これで問題は解決しました。
挿入するBeanにアノテーション@GeneratedValueを追加します。
上記の問題を解決する1つの方法は、hashcode()
をオーバーライドすることです。
また、保存の前後に休止状態セッションをフラッシュします。
getHibernateTemplate().flush();
デタッチされたオブジェクトを明示的にnull
に設定することも役立ちます。
これは、同じオブジェクトへの参照を使用して、テーブル内の複数の行を保存しようとしていることを意味します。
エンティティクラスのidプロパティを確認してください。
@Id
private Integer id;
に
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
Hibernateで「Cascade」属性を見つけて削除します。 「カスケード」を使用可能に設定すると、関連するクラスと関係がある別のエンティティで他の操作(保存、更新、削除)が呼び出されます。したがって、同じID値が発生します。それは私と一緒に働いた。
このメッセージに出会ったのは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が説明したものです。はい、それは最良の答えです。これがどのように起こるかの一例。
前にクエリのコードを配置してみてください。それは私の問題を修正します。例えばこれを変更します:
query1
query2 - get the error
update
これに:
query2
query1
update
entityRepositoryを使用する場合、saveの代わりにsaveAndFlushを使用します
IDEの式タブを開いたままにしておくと、この例外の原因となるオブジェクトに対して休止状態のget呼び出しが行われていました。この同じオブジェクトを削除しようとしていました。また、このエラーを発生させるために必要と思われる削除呼び出しにブレークポイントがありました。別の式タブをフロントタブにするか、IDEがブレークポイントで停止しないように設定を変更するだけで、この問題は解決しました。
このような行を挿入すると、主キーの生成が間違っているために問題に遭遇しました:
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>
私の場合、flush()のみが機能しませんでした。 flush()の後にclear()を使用する必要がありました。
public Object merge(final Object detachedInstance)
{
this.getHibernateTemplate().flush();
this.getHibernateTemplate().clear();
try
{
this.getHibernateTemplate().evict(detachedInstance);
}
}
更新クエリを呼び出す前にオブジェクトの識別子を設定していない可能性があります。