EntityManager.remove()のメモ
削除の動作を確認しようと次のようなコードを実行したところ、ObjectDeletedExceptionという例外がthrowされた。
Remove.class
package sample; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; public class RemoveExample { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("hibernate"); EntityManager em = emf.createEntityManager(); final EntityTransaction tx = em.getTransaction(); tx.begin(); Actor brad = new Actor("Brad Pitt"); Actor orlando = new Actor("Orlando Bloom"); em.persist(brad); em.persist(orlando); Movie troy = new Movie("Troy"); troy.addActor(brad); troy.addActor(orlando); em.persist(troy); em.remove(brad); tx.commit(); em.close(); emf.close(); } }
例外
Exception in thread "main" javax.persistence.RollbackException: Error while commiting the transaction at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71) at sample.RemoveExample.main(RemoveExample.java:32) Caused by: org.hibernate.ObjectDeletedException: deleted entity passed to persist: [sample.Actor#] at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:90) at org.hibernate.impl.SessionImpl.firePersistOnFlush(SessionImpl.java:644) at org.hibernate.impl.SessionImpl.persistOnFlush(SessionImpl.java:636) at org.hibernate.engine.CascadingAction$9.cascade(CascadingAction.java:323) at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268) at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216) at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169) at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296) at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242) at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219) at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169) at org.hibernate.engine.Cascade.cascade(Cascade.java:130) at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131) at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122) at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65) at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26) at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000) at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338) at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106) at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:54) ... 1 more
どうも「em.remove(brad);」を呼び出したはいいが、troyインスタンス(Movie)がbradインスタンス(Actor)の参照を保持したままになっているのがいけないらしい。
ということで、まずMovieクラスにActorを削除する為のメソッドを追加。
Movie.class(抜粋)
public boolean removeActor(Actor actor) { return actors.remove(actor); }
そして、「em.remove(brad);」を呼び出したあとで、bradインスタンス(Actor)の参照をtroyインスタンス(Movie)から削除するように修正。
Remove.class(抜粋)
em.remove(brad); troy.removeActor(brad);
実行後、期待通り「Brad Pitt」は登録されず、「Orlando Bloom」と「Troy」が登録されることを確認。
SELECT * FROM ACTOR ;
ID | NAME |
---|---|
54 | Orlando Bloom |
SELECT * FROM MOVIE_ACTOR ;
MOVIE_ID | ACTOR_ID |
---|---|
65 | 54 |
SELECT * FROM MOVIE;
ID | NAME |
---|---|
65 | Troy |