Hello forum,
this time I have a complex problem getting may object graph deleted properly. I have left out some parts of object relations since they are very complex and large in reality and would complicate understanding of this post.
What I have I a central parent class called 'Controller'. Controller has a two OneToMany relations with join tables:
Code:
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "ControllerInterface", joinColumns = @JoinColumn(name = "controllerId"), inverseJoinColumns = @JoinColumn(name = "interfaceId"))
@Fetch(FetchMode.SUBSELECT)
@OrderColumn(name = "interfaceIndex")
private final List<HardwareInterface> hardwareInterfaces = new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "ControllerHardwareModule", joinColumns = @JoinColumn(name = "controllerId"), inverseJoinColumns = @JoinColumn(name = "hardwareModuleId"))
@Fetch(FetchMode.SUBSELECT)
@OrderColumn(name = "moduleIndex")
private final List<HardwareModule> hardwareModules = new ArrayList<>();
Both of the mapped classes are actually base classes to concrete classes. So there is a 'SerialInterface' class derived from HardwareInterface and an 'Extension' class derived from HardwareModule that uses one of the controller's hardware interfaces. Lets take a look at Extension which has a ManyToOne relation to hardware interface:
Code:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = PROPERTY_HARDWARE_INTERFACE, nullable = false)
private HardwareInterface hardwareInterface;
Now the problem seems to be that when I delete the controller, hibernate issues the cascading deletes in the wrong order. It trys to first delete the hardware interfaces which fails with a constraint violation exception in the Extension table.
Delete statement sequence from log file:
Quote:
delete from ControllerInterface where controllerId=?
... left out some unnecessary deletes ...
delete from SerialInterface where id=?
delete from HardwareInterface where id=? and version=?
The stack trace is as follows:
Quote:
org.springframework.dao.DataIntegrityViolationException: The DELETE statement conflicted with the REFERENCE constraint "FK31BF5B4B2C9DE801". The conflict occurred in database "test", table "dbo.Extension", column 'hardwareInterface'.; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: The DELETE statement conflicted with the REFERENCE constraint "FK31BF5B4B2C9DE801". The conflict occurred in database "test", table "dbo.Extension", column 'hardwareInterface'.
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:159)
at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:606)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:488)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy140.delete(Unknown Source)
From what I have seen so far I guess that it is a problem, that the relation in the HardwareModule is unidirectional. So Hibernate does not know that it should delete the HardwareInterfaces first. But I cannot make the relation bidirectional because there are other classes using HardwareInterfaces that have no common base class that I could use to materialize the not-owning side.
Has anyone had a similiar scenario and found a solution to this? Sure I could introduce such a base class but that would cause even more joins when reading the database then I already have.