Hibernate version: core: 3.2.5.ga, annotations: 3.3.0.ga
I've search over the forum and even if there's lots of post on this subject, I didn't find any answer for my problem...
I have a bean with a many to many relation on itself.
When I delete a bean which is related to other beans, it cannot perform the delete because of a foreign key constraint violation:
org.hibernate.exception.ConstraintViolationException.
Code:
org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:622)
at org.springframework.orm.hibernate3.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:690)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:566)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:346)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:199)
at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:340)
at org.springframework.test.context.junit4.SpringMethodRoadie.runAfters(SpringMethodRoadie.java:351)
at org.springframework.test.context.junit4.SpringMethodRoadie.runBeforesThenTestThenAfters(SpringMethodRoadie.java:262)
at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:234)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:204)
at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:146)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:151)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:237)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:146)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
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.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:558)
... 22 more
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`obs/test_test`, CONSTRAINT `FK5C8CE6BFAAF990FE` FOREIGN KEY (`relatedtests_id`) REFERENCES `test` (`id`))
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1213)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:912)
at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
... 30 more
Here is my test bean :
Code:
@Entity
public class Test implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany
private Set<Test> relatedTests;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<Test> getRelatedTests() {
if (relatedTests == null) relatedTests = new HashSet<Test>();
return relatedTests;
}
public void setRelatedTests(Set<Test> relatedTests) {
this.relatedTests = relatedTests;
}
public void addRelatedTest(Test test) {
if (getRelatedTests().add(test)) test.getRelatedTests().add(this);
}
public void removeRelatedTest(Test test) {
if (getRelatedTests().remove(test)) test.getRelatedTests().remove(this);
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof Test)) return false;
Test castOther = (Test) other;
return new EqualsBuilder().append(id, castOther.id).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(id).toHashCode();
}
}
Test case:
Code:
[...]
@Test
@Rollback(false)
public void createTest() {
Test t1 = new Test();
genericDao.saveOrUpdate(t1);
Test t2 = new Test();
genericDao.saveOrUpdate(t2);
t1.addRelatedTest(t2);
genericDao.saveOrUpdate(t1);
}
@Test
@Rollback(false)
public void deleteTest() {
Test test = genericDao.findAll(Test.class).get(0);
genericDao.delete(test);
}
[...]
The only work-around I found is to create an Interceptor that clear the relation beofre a bean is deleted :
Code:
public class TestInterceptor extends EmptyInterceptor {
@Override
public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
if (entity instanceof Test) {
Test test = (Test) entity;
for (Test related : test.getRelatedTests()) {
test.removeRelatedTest(related);
}
}
}
}
I'm sure there's a way to avoid this Interceptor... I don't undersatnd with this many to many relation cannot be deleted on cascade :-(
I've also tried with @Cascade but it does not work neither...
Thanks for your help.