我有一个 ContactEntity(父)和 PhoneNumberComponent(子)关系。当我尝试 EntityManager.remove() 孩子时,我得到下面的异常。
另外,我注意到,如果我在 AbstractContactEntity 的 phoneNumbers 映射中删除 CascadeType.Persist 或将 FetchType.EAGER 更改为 LAZY,我的 JUnit 就会通过。
有人能告诉我我错过了什么吗?
使用:休眠,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)
联系实体(父)
@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 child)
@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 中看到的最后一个 em.flush 上的 JUnit 炸弹。另一个观察结果是,如果我用下面的代码替换 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();
}