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