Reputation: 1
I am using Hibernate 4.3.11 and attempting to persist an entity with a one-to-one relationship cascading to another entity but Hibernate appears to be attempting the persistence in the wrong order which results in a ConstraintViolationException.
Here are my entities:
@Entity
@Table(name = "B2B_ORDER")
public class Order implements java.io.Serializable
{
@Id
@Column(name = "ORDER_NUMBER", unique = true, nullable = false, length = 32)
private String orderNumber;
@OneToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "order")
private OrderCustomer orderCustomer;
...
}
@Entity
@Table(name = "B2B_ORDER_CUSTOMER")
public class OrderCustomer implements java.io.Serializable
{
@Id
@Column(name = "ORDER_NUMBER")
private String orderNumber;
@OneToOne(optional = false)
@JoinColumn(name = "ORDER_NUMBER", referencedColumnName = "ORDER_NUMBER")
private Order order;
...
}
All I'm trying to do is create an Order with an OrderCustomer and persist the Order which should also create the OrderCustomer record:
Order order = new Order();
order.setOrderNumber(orderNumber);
...
OrderCustomer orderCustomer = new OrderCustomer();
orderCustomer.setOrder(order);
orderCustomer.setOrderNumber(order.getOrderNumber());
order.setOrderCustomer(orderCustomer);
...
order = orderRepository.save(order);
Note that orderRepository is a Spring repository object and all the save method does is call entityManager.persist(entity). When I run this code I hit an integrity constraint violation in the DB due to the foreign key:
2016-08-24 15:03:00,425 DEBUG [org.hibernate.SQL] (http-nio-9090-exec-1) [SqlStatementLogger.java:109] insert into b2b_order_customer (customer_profile_txt, order_number) values (?, ?) 2016-08-24 15:03:00,606 WARN [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-nio-9090-exec-1) [SqlExceptionHelper.java:144] SQL Error: -530, SQLState: 23503 2016-08-24 15:03:00,607 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-nio-9090-exec-1) [SqlExceptionHelper.java:146] DB2 SQL Error: SQLCODE=-530, SQLSTATE=23503, SQLERRMC=CWSODEV2.B2B_ORDER_CUSTOMER.B2B_ORDER_CUSTOMER_ORD_NUM_FK, DRIVER=4.15.100 2016-08-24 15:03:00,612 INFO [org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl] (http-nio-9090-exec-1) [AbstractBatchImpl.java:208] HHH000010: On release of batch it still contained JDBC statements 2016-08-24 15:03:00,721 ERROR [...] (http-nio-9090-exec-1) [OrderProcessor.java:188] Error persisting the order: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:259) ~[spring-orm-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225) ~[spring-orm-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417) ~[spring-orm-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) ~[spring-tx-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) ~[spring-tx-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) ~[spring-tx-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131) ~[spring-data-jpa-1.9.1.RELEASE.jar!/:na] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at com.sun.proxy.$Proxy111.save(Unknown Source) ~[na:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91] at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_91] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] ... at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91] at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91] at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_91] at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.3.RELEASE.jar!/:4.2.3.RELEASE] at com.sun.proxy.$Proxy113.save(Unknown Source) ~[na:na] ... Caused by: com.ibm.db2.jcc.am.SqlIntegrityConstraintViolationException: DB2 SQL Error: SQLCODE=-530, SQLSTATE=23503, SQLERRMC=CWSODEV2.B2B_ORDER_CUSTOMER.B2B_ORDER_CUSTOMER_ORD_NUM_FK, DRIVER=4.15.100 at com.ibm.db2.jcc.am.fd.a(fd.java:692) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.fd.a(fd.java:60) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.fd.a(fd.java:127) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.qo.b(qo.java:2412) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.qo.c(qo.java:2395) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.t4.ab.l(ab.java:374) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.t4.ab.a(ab.java:61) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.t4.p.a(p.java:50) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.t4.rb.b(rb.java:220) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.ro.qc(ro.java:3526) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.ro.b(ro.java:4489) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.ro.ic(ro.java:807) ~[db2jcc4-4.15.100.jar!/:na] at com.ibm.db2.jcc.am.ro.executeUpdate(ro.java:781) ~[db2jcc4-4.15.100.jar!/:na] at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208) ~[hibernate-core-4.3.11.Final.jar!/:4.3.11.Final] ... 205 common frames omitted
Clearly Hibernate is trying to persist OrderCustomer before Order. This is the opposite order that it should be persisted. If you consider a OneToMany cascading relationship, the single entity would always be persisted before the collection (and thus the foreign keys are satisfied). Why does this not work for a one-to-one?
Thanks.
Upvotes: 0
Views: 1927
Reputation: 61
The problem is the optional
parameter. Why ? When this parameter is false
, then Hibernate 'trust you' and think that DB contains related entity.
You are using optional = false
in both entities. This is the problem.
When both entities have optional = false
:
Hibernate: create table B2B_ORDER (ORDER_NUMBER varchar(32) not null, primary key (ORDER_NUMBER))
Hibernate: create table B2B_ORDER_CUSTOMER (ORDER_NUMBER varchar(32) not null, primary key (ORDER_NUMBER))
Hibernate: alter table B2B_ORDER_CUSTOMER add constraint FKsvt9d7j362lni8mxb83y7cygj foreign key (ORDER_NUMBER) references B2B_ORDER
Hibernate: insert into B2B_ORDER_CUSTOMER (ORDER_NUMBER) values (?)
ERROR :( Order table is empty.
When only OrderCustomer has optional = false
:
Hibernate: create table B2B_ORDER (ORDER_NUMBER varchar(32) not null, primary key (ORDER_NUMBER))
Hibernate: create table B2B_ORDER_CUSTOMER (ORDER_NUMBER varchar(32) not null, primary key (ORDER_NUMBER))
Hibernate: alter table B2B_ORDER_CUSTOMER add constraint FKsvt9d7j362lni8mxb83y7cygj foreign key (ORDER_NUMBER) references B2B_ORDER
Hibernate: insert into B2B_ORDER (ORDER_NUMBER) values (?)
Hibernate: insert into B2B_ORDER_CUSTOMER (ORDER_NUMBER) values (?)
SUCCESS :)
When only Order has optional = false
:
Hibernate: create table B2B_ORDER (ORDER_NUMBER varchar(32) not null, primary key (ORDER_NUMBER))
Hibernate: create table B2B_ORDER_CUSTOMER (ORDER_NUMBER varchar(255) not null, primary key (ORDER_NUMBER))
Hibernate: insert into B2B_ORDER_CUSTOMER (ORDER_NUMBER) values (?)
Hibernate: insert into B2B_ORDER (ORDER_NUMBER) values (?)
SUCCESS :)
You can see that @OneToOne
on the owning side (OrderCustomer
) override Order
@OneToOne
optional
parameter
Upvotes: 0
Reputation: 46
From JPA reference:
For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key
In this case the OrderCustomer
is the owning side.
You can move cascade
parameter to OrderCustomer
and save OrderCustomer
instead of Order
.
You can move @JoinColumn
to Order
table.
Upvotes: 0