2

ContactEntity(親)とPhoneNumberComponent(子)の関係があります。子をEntityManager.remove()しようとすると、以下の例外が発生します。

また、AbstractContactEntityのphoneNumbersマッピングでCascadeType.Persistを削除するか、FetchType.EAGERをLAZYに変更すると、JUnitが渡されることに気付きました。

誰かが私が欠けているものを教えてもらえますか?

使用:hibernate、hsqldb

スタックトレース

javax.persistence.EntityNotFoundException: deleted entity passed to persist: [com.bb.sc.contact.jpa.entity.PhoneNumberComponent#<null>]
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1329)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1280)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1286)
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:969)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
    at $Proxy35.flush(Unknown Source)
    at test.bb.sc.contact.jpa.entity.PhoneNumberComponentJUnit.testCRUD(PhoneNumberComponentJUnit.java:98)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

ContactEntity(親)

@Entity
@Table (name="CONTACT")
@Inheritance (strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn (name="CONTACT_DISCRIMANATOR")
public abstract class AbstractContactEntity implements Auditable {
    @Column (name="CONTACT_ID", nullable=false)
    @Id @GeneratedValue (strategy=GenerationType.IDENTITY)
    private Long id;
    @OneToMany (mappedBy="contact", cascade=CascadeType.ALL, fetch=FetchType.LAZY, targetEntity=PostalAddressComponent.class)
    private Collection<PostalAddressComponent> postalAddresses;
    @OneToMany (mappedBy="contact", targetEntity=PhoneNumberComponent.class, cascade={CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.PERSIST}, fetch=FetchType.EAGER)
    private Collection<PhoneNumberComponent> phoneNumbers;
    @OneToMany (mappedBy="contact", cascade=CascadeType.ALL, fetch=FetchType.LAZY, targetEntity=EmailAddressComponent.class)
    private Collection<EmailAddressComponent> emailAddresses;
    @Embedded
    private DbAudit dbAudit;

ContactDetail(抽象子)

@MappedSuperclass
public abstract class AbstractContactDetail implements Auditable{
    @Id @GeneratedValue (strategy=GenerationType.IDENTITY)
    private Long id;
    @OneToOne @JoinColumn (name="CONTACT_TYPE_ID", nullable=false)
    private ContactDetailTypeEntity type;
    @ManyToOne (targetEntity=AbstractContactEntity.class) @JoinColumn (name="CONTACT_ID", nullable=false)
    private AbstractContactEntity contact;
    @Embedded
    private DbAudit dbAudit;

ContactDetail impl(具体的なimplの子)

@Entity
@Table (name="PHONE_NUMBER")
@AttributeOverride (name="id", column=@Column(name="PHONE_NUMBER_ID"))
public class PhoneNumberComponent extends AbstractContactDetail {

    @Column (name="AREA_CODE", nullable=false)
    private Integer areaCode;
    @Column (name="PREFIX", nullable=false)
    private Integer prefix;
    @Column (name="SUFFIX", nullable=false)
    private Integer suffix;
    @Column (name="EXTENSION")
    private Integer ext;

JUnit

@Before
public void setup () {
    contact = new ContactPersonEntity();
    type = new ContactDetailTypeEntity();

    type.setCode("CODE");
    type.setType("TYPE");
    em.persist(contact);
    em.persist(type);
    em.flush();

    // Before each test, make sure the EntityManager has a clean persistence context.
    em.clear();
}   
@Test
public void testDelete () {
    PhoneNumberComponent p = new PhoneNumberComponent(111, 222, 3333, 44, contact, type);
    em.persist(p);
    em.flush();
    assertNotNull (p.getId());

    em.clear();
    PhoneNumberComponent found = em.find(PhoneNumberComponent.class, p.getId());
    assertNotNull (found);
    em.remove(found);
    em.flush();
}

JUnitは、私のJUnitに表示される最後のem.flushで爆弾を投下します。もう1つの観察結果は、JUnitのem.remove(foundUpd)行を以下のコードに置き換えると、テストに合格することです。ただし、レコードを削除するためだけに、親オブジェクトグラフをロードしてトラバースし、親子リンクを明示的に解除する必要はありません。ありがとう。

    AbstractContactEntity c = em.find(AbstractContactEntity.class, 1L);
    c.getPhoneNumbers();
    Iterator<PhoneNumberComponent> it = c.getPhoneNumbers().iterator();
    while (it.hasNext()) {
        PhoneNumberComponent p = it.next();
        em.remove(p);
        it.remove();
    }
4

2 に答える 2

3

FetchType.EAGER マッピングでは、子を永続コンテキストにロードすると、親もロードされます。私の JUnit では、子を見つける前に EntityManager.clear を実行しました。子が永続化コンテキスト内の唯一のエンティティであると考えるのは、不適切な想定でした。

デバッグで、実行しました

PhoneNumberComponent found = em.find(PhoneNumberComponent.class, p.getId());

生成された SQLのペアに気付きました。

Hibernate: select phonenumbe0_.PHONE_NUMBER_ID as PHONE1_3_2_, phonenumbe0_.CONTACT_ID as CONTACT10_3_2_, phonenumbe0_.CREATE_TIMESTAMP as CREATE2_3_2_, phonenumbe0_.CREATE_USER as CREATE3_3_2_, phonenumbe0_.UPDATE_TIMESTAMP as UPDATE4_3_2_, phonenumbe0_.UPDATE_USER as UPDATE5_3_2_, phonenumbe0_.CONTACT_DETAIL_TYPE_ID as CONTACT11_3_2_, phonenumbe0_.AREA_CODE as AREA6_3_2_, phonenumbe0_.EXTENSION as EXTENSION3_2_, phonenumbe0_.PREFIX as PREFIX3_2_, phonenumbe0_.SUFFIX as SUFFIX3_2_, abstractco1_.CONTACT_ID as CONTACT2_0_0_, abstractco1_.CREATE_TIMESTAMP as CREATE3_0_0_, abstractco1_.CREATE_USER as CREATE4_0_0_, abstractco1_.UPDATE_TIMESTAMP as UPDATE5_0_0_, abstractco1_.UPDATE_USER as UPDATE6_0_0_, abstractco1_.ORGANIZATION_NAME as ORGANIZA7_0_0_, abstractco1_.FRIST_NAME as FRIST8_0_0_, abstractco1_.LAST_NAME as LAST9_0_0_, abstractco1_.CONTACT_DISCRIMANATOR as CONTACT1_0_0_, contactdet2_.CONTACT_DETIAL_TYPE_ID as CONTACT1_1_1_, contactdet2_.CODE as CODE1_1_, contactdet2_.DESCRIPTION as DESCRIPT3_1_1_, contactdet2_.TYPE as TYPE1_1_ from PHONE_NUMBER phonenumbe0_ inner join CONTACT abstractco1_ on phonenumbe0_.CONTACT_ID=abstractco1_.CONTACT_ID inner join CONTACT_TYPE contactdet2_ on phonenumbe0_.CONTACT_DETAIL_TYPE_ID=contactdet2_.CONTACT_DETIAL_TYPE_ID where phonenumbe0_.PHONE_NUMBER_ID=?
Hibernate: select phonenumbe0_.CONTACT_ID as CONTACT10_0_2_, phonenumbe0_.PHONE_NUMBER_ID as PHONE1_2_, phonenumbe0_.PHONE_NUMBER_ID as PHONE1_3_1_, phonenumbe0_.CONTACT_ID as CONTACT10_3_1_, phonenumbe0_.CREATE_TIMESTAMP as CREATE2_3_1_, phonenumbe0_.CREATE_USER as CREATE3_3_1_, phonenumbe0_.UPDATE_TIMESTAMP as UPDATE4_3_1_, phonenumbe0_.UPDATE_USER as UPDATE5_3_1_, phonenumbe0_.CONTACT_DETAIL_TYPE_ID as CONTACT11_3_1_, phonenumbe0_.AREA_CODE as AREA6_3_1_, phonenumbe0_.EXTENSION as EXTENSION3_1_, phonenumbe0_.PREFIX as PREFIX3_1_, phonenumbe0_.SUFFIX as SUFFIX3_1_, contactdet1_.CONTACT_DETIAL_TYPE_ID as CONTACT1_1_0_, contactdet1_.CODE as CODE1_0_, contactdet1_.DESCRIPTION as DESCRIPT3_1_0_, contactdet1_.TYPE as TYPE1_0_ from PHONE_NUMBER phonenumbe0_ inner join CONTACT_TYPE contactdet1_ on phonenumbe0_.CONTACT_DETAIL_TYPE_ID=contactdet1_.CONTACT_DETIAL_TYPE_ID where phonenumbe0_.CONTACT_ID=?

次に、次の行を追加しましたが、コンソールに SQL がないことに気付きました。EntityManager が永続化コンテキストで親を見つけることができたので、DB アクションは必要ありませんでした。

AbstractContactEntity c = em.find(AbstractContactEntity.class, 1L);

AbstractContactEntity の phoneNumbers マッピングを FetchType.LAZY に変更すると、最初の SQL しか取得できません。

編集

Hibernate がこれを行う理由はわかりませんが、今のところ、動作を受け入れてそれに応じて設計しています。

これは、Hibernate でおそらく何が起こっているかを示しています。

子エンティティを find() すると、親も永続コンテキストにロードされます。次に、remove() は子に対する削除命令と親に対する更新命令をマークします。最後に、flush() すると、子が削除され、親が DB に戻されます。しかし、その親にはまだ削除された子への参照があるため、EntityNotFoundException: deleted entity passed to persist でキックされます。

于 2012-06-16T11:59:27.190 に答える
0

ContactEntity (親) と PhoneNumberComponent (子)

ContactEntity に PhoneNumberComponent のリストがあります。子を削除するには、次の手順に従います。

  1. DB から PhoneNumberComponent を取得します。
  2. DB からそれぞれの ContactEntity を取得します。
  3. ContactEntity リストからそれぞれの PhoneNumberComponent を削除します。
  4. 今すぐ PhoneNumberComponent を削除します
于 2012-06-24T05:27:17.170 に答える