-1

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.

Vlasec
  • 5,500
  • 3
  • 27
  • 30

1 Answers1

-1
@OneToOne
@JoinColumn(name = "payment_id")
@ForeignKey
private Payment payment;

You can use @ForeignKey annotation here. Edited --

@OneToOne
@JoinColumn(name = "payment_id", foreignKey = @ForeignKey(name = "FK_NAME"))
private Payment payment;
Jahangir Alam
  • 311
  • 2
  • 11
  • The annotation `@ForeignKey` cannot be used on a field or a method. It is supposed to be used inside `@JoinColumn` annotation. Or you mean the deprecated hibernate one? – Vlasec Feb 24 '16 at 10:40
  • Are you Sure Viasec? I have tested it in my idea. – Jahangir Alam Feb 24 '16 at 10:41
  • Uh ok, found two different annotations of that name. One deprecated in `org.hibernate.annotations`, the other is `javax.persistence` and it can't be used on fields or methods. I may try, but I'm afraid mixing up deprecated Hibernate annotations with JPA might cause problems. – Vlasec Feb 24 '16 at 10:45