Using Spring Data JPA from Spring version 4.0.7, backed by Hibernate (judging by import of Envers 4.3.6), I wanted to create a one-to-one relation between two entities - Payment and PaymentFailure.
When a Payment fails, PaymentFailure is created with ID of Payment. Trying to do that, I encounter various errors when using any recommended code snippets found on StackOverflow and elsewhere.
E.g., when using @Id
and @MapsId
, I encountered DataIntegrityViolationException
:
org.springframework.web.util.NestedServletException: Request processing failed;
nested exception is org.springframework.dao.DataIntegrityViolationException:
A different object with the same identifier value was already associated
with the session : [blah.payment.PaymentFailure#172656];
nested exception is javax.persistence.EntityExistsException:
A different object with the same identifier value was already associated
with the session : [blah.payment.PaymentFailure#172656]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:62)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:170)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:137)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:141)
Payment entity looks like that:
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;
import javax.persistence.*;
import java.io.Serializable;
@Audited
@Entity
@Table(name = "payment")
@DynamicInsert
public class Payment implements Serializable {
@Id
@GeneratedValue
private Long id;
@NotAudited
@OneToOne(mappedBy = "payment", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private PaymentFailure paymentFailure;
// ... other fields, setters ...
}
And this is PaymentFailure:
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "payment_failure")
public class PaymentFailure implements Serializable {
@Id
private Long id;
@MapsId
@OneToOne
@JoinColumn(name = "payment_id")
private Payment payment;
// ... getters, setters, other fields
}
I also tried to modify it, someone recommended a nicer version meintioned in guides for Hibernate JPA, where you don't have a separate field for ID:
@Id
@OneToOne
@JoinColumn(name = "payment_id")
private Payment payment;
That however ends in ApplicationContext failing to load, because of IllegalArgumentException
saying that annotation @IdClass
is missing:
Caused by: java.lang.IllegalArgumentException:
This class [class blah.payment.PaymentFailure] does not define an IdClass
at org.hibernate.jpa.internal.metamodel
.AbstractIdentifiableType.getIdClassAttributes(AbstractIdentifiableType.java:200)
at org.springframework.data.jpa.repository.support
.JpaMetamodelEntityInformation$IdMetadata.<init>(JpaMetamodelEntityInformation.java:221)
at org.springframework.data.jpa.repository.support
.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:79)
at org.springframework.data.jpa.repository.support
.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:65)
at org.springframework.data.jpa.repository.support
.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:149)
at org.springframework.data.jpa.repository.support
.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:88)
at org.springframework.data.jpa.repository.support
.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:68)
at org.springframework.data.repository.core.support
.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:158)
at org.springframework.data.repository.core.support
.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:224)
at org.springframework.data.repository.core.support
.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:210)
at org.springframework.data.jpa.repository.support
.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
at org.springframework.beans.factory.support
.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1613)
at org.springframework.beans.factory.support
.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1550)
Is there some approach that would work in my setup? Or, possibly, does it work with higher version of Spring or Hibernate? I'm really lost now, I've read plenty of advices, but none works.