Hi,
I use Hibernate 3.6.4.
I have 2 basic entities "Parent" and "Child" with bidirectional orphanRemoval OneToOne association between them.
PropertyAccessException is thrown when I want to remove Child entity from Parent.
...
parent.setChild(null);
entityManager.merge(parent);
entityManager.flush();
...
When I try the same code with eclipselink 2.1.0 everything is ok. Parent has "null" child and child entity is deleted from db.
I can send by email simple working maven project with unittest for both jpa providers.
Code:
org.springframework.orm.hibernate3.HibernateSystemException: IllegalArgumentException occurred calling getter of entity.Parent.id; nested exception is org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entity.Parent.id
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:679) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:102) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:471) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) [spring-tx-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) [spring-tx-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.endTransaction(TransactionalTestExecutionListener.java:515) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:290) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:183) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:406) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:90) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) [junit-dep-4.8.2.jar:na]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) [junit-dep-4.8.2.jar:na]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) [junit-dep-4.8.2.jar:na]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) [junit-dep-4.8.2.jar:na]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) [junit-dep-4.8.2.jar:na]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) [junit-dep-4.8.2.jar:na]
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.junit.runners.ParentRunner.run(ParentRunner.java:236) [junit-dep-4.8.2.jar:na]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180) [spring-test-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) [.cp/:na]
Caused by: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entity.Parent.id
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:198) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:227) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3875) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:3583) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:203) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:101) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:965) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:948) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:258) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.engine.Cascade.cascade(Cascade.java:161) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:154) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:145) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:88) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76) ~[hibernate-entitymanager-3.6.4.Final.jar:3.6.4.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467) ~[spring-orm-3.0.5.RELEASE.jar:3.0.5.RELEASE]
... 25 common frames omitted
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_23]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_23]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_23]
at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_23]
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:172) ~[hibernate-core-3.6.4.Final.jar:3.6.4.Final]
... 43 common frames omitted
Code:
package entity;
import javax.persistence.*;
@Entity
public class Parent implements BaseEntity {
private static final long serialVersionUID = 1L;
@Column
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String value;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private Child child;
@Override
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
Code:
package entity;
import javax.persistence.*;
@Entity
public class Child implements BaseEntity {
private static final long serialVersionUID = 1L;
@Column
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String value;
@OneToOne(fetch = FetchType.LAZY)
private Parent parent;
@Override
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
Code:
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import dao.Dao;
import entity.Child;
import entity.Parent;
@ContextConfiguration(locations = {
"classpath:META-INF/spring/config-dao.xml"})
public class OneToOneIntegrationTests extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
Dao dao;
private Parent parent;
private Child child;
@BeforeTransaction
public void prepareData() {
parent = new Parent();
parent.setValue("parent value");
Child newChild = new Child();
newChild.setValue("child value");
newChild.setParent(parent);
parent.setChild(newChild);
parent = (Parent) dao.save(parent);
child = parent.getChild();
}
@Test
@Rollback(value = false)
public void removeChild() {
assertThat(parent, notNullValue());
parent.setChild(null);
parent = (Parent) dao.save(parent);
assertThat(parent, notNullValue());
assertThat(parent.getChild(), nullValue());
}
@AfterTransaction
public void verifyChildRemoved() {
Child foundChild = (Child) dao.findById(Child.class, child.getId());
assertThat(foundChild, nullValue());
}
@AfterTransaction
public void cleanData(){
dao.delete(parent);
}
}