親子関係のあるアプリケーションがあります。すべての親には一連の子 (1 対多) があり、すべての子には親 (多対 1) があります。アプリケーションにはサービスレイヤーがあり、そこでほとんど次のことを行います。
public void addChild() {
parent = getParentFromDB();
Child child = new Child();
child.setParent(parent);
saveChild(child);
Set<Child> children = new HashSet<Child>();
children.addAll(parent.getChildren());
children.add(child);
parent.setChildren(children); // notice this line here.
saveParent(parent);
doStuff(parent);
}
サービス層は、@Transactional で注釈が付けられた通常の Spring Bean であるファサードから呼び出されます。メソッドを呼び出そうとするとaddChild()
、次の結果が得られます。
2013-03-05 17:09:50,195 [qtp511931089-83] WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
2013-03-05 17:09:50,196 [qtp511931089-83] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry '25-9' for key 'PRIMARY'
2013-03-05 17:09:50,198 [qtp511931089-83] ERROR org.hibernate.engine.jdbc.batch.internal.BatchingBatch - HHH000315: Exception executing batch [Duplicate entry '25-9' for key 'PRIMARY']
2013-03-05 17:09:50.204:WARN:oejs.ServletHandler:/child/add
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Duplicate entry '25-9' for key 'PRIMARY'
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1306)
SQLログを休止状態にしましたが、休止状態がsaveを2回呼び出しているようです!!! 「//notice this line here」行を削除すると、すべて正常に機能します。エンティティはデータベースに保存され、DB で 25-9 の関係を確認できます。問題は、メソッドの最後に doStuff(parent); があることです。親とその子でいくつかのことを行うメソッドなので、「//ここの行に注意してください」行を削除できません
編集: これは、親が子で宣言される方法です:
@ManyToOne(fetch = FetchType.EAGER, targetEntity = ParentModel.class, cascade = { CascadeType.REMOVE })
@JoinTable(name = "PARENT_CHILDREN", joinColumns = @JoinColumn(name = "CHILD_ID"), inverseJoinColumns = @JoinColumn(name = "PARENT_ID"))
private ParentModel parent;
これは、子が親で宣言される方法です。
@OneToMany(fetch = FetchType.LAZY, targetEntity = ChildModel.class, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE })
@JoinTable(name = "PARENT_CHILDREN", joinColumns = @JoinColumn(name = "PARENT_ID"), inverseJoinColumns = @JoinColumn(name = "CHILD_ID"))
private Set<ChildModel> children;