0

I have problem with saving A object with not null b property to database. I set cascade to all so I expect it that it will save B object as well. If I try to use entityManager.merge(a) method, objects save without error, but when I call simple 'select' on database to check if the records in database are correct I see that ids of the corresponding rows in table_a and table_b are different. How should I fix my code to count that just by managing A objects B objects will be saved properly as well?

JPA provider: Hibernate

Darabase: PostgreSQL

My code:

@Entity
@Table(name = "table_a")
public class A {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @JoinColumn(name = "id", referencedColumnName = "id")
    @OneToOne(cascade = {CascadeType.ALL}, orphanRemoval = true)
    private B b;

    ...
}

@Entity
@Table(name = "table_b")
public class B {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    ...
}

My tables:

create table table_a (
    id BIGSERIAL PRIMARY KEY,
    ...
);

create table table_b (
    id BIGSERIAL PRIMARY KEY REFERENCES table_a(id),
    ...
);

[Edit after Adam's Michalik answer]

My current code:

@Entity
@Table(name = "table_a")
public class A {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @OneToOne(mappedBy = "a", cascade = {CascadeType.ALL})
    private B b;

    ...
}

@Entity
@Table(name = "table_b")
public class B {

    @Id
    @JoinColumn(name = "id", referencedColumnName = "id")
    @OneToOne(cascade = {CascadeType.ALL})
    private A a;

    ...
}

My tables:

create table table_a (
    id BIGSERIAL PRIMARY KEY,
    ...
);

create table table_b (
    id BIGINT PRIMARY KEY REFERENCES table_a(id),
    ...
);

Now I get this exception opon calling entityManager.merge(a):

Exception in thread "main" javax.ejb.EJBTransactionRolledbackException
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleInCallerTx(CMTTxInterceptor.java:163)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:253)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:342)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:239)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:43)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:95)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:55)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:64)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:326)
    at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:439)
    at org.jboss.invocation.AccessCheckingInterceptor.processInvocation(AccessCheckingInterceptor.java:61)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:326)
    at org.jboss.invocation.PrivilegedWithCombinerInterceptor.processInvocation(PrivilegedWithCombinerInterceptor.java:80)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:185)
    at org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:182)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:309)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:73)
...
Caused by: java.lang.NullPointerException
    at org.hibernate.type.descriptor.java.AbstractTypeDescriptor.extractHashCode(AbstractTypeDescriptor.java:84)
    at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:216)
    at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:220)
    at org.hibernate.type.EntityType.getHashCode(EntityType.java:391)
    at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:258)
    at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:76)
    at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:71)
    at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:327)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:166)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:886)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:868)
    at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:277)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:460)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:255)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:189)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196)
    at org.jboss.as.jpa.container.AbstractEntityManager.merge(AbstractEntityManager.java:566)
3
  • There is an excellent tutorial on OneToOne here mkyong.com/hibernate/… Commented Jan 8, 2016 at 14:02
  • what "ids are different"? Commented Jan 8, 2016 at 14:02
  • Just edited main post where I try to clarify what i mean by 'ids are different'. Commented Jan 8, 2016 at 14:09

1 Answer 1

2

There are a few issues here:

  1. In the entity model, you have a unidirectional one-to-one relationship from A to B. That would place the foreign key to B in A. But in the DB schema, you have placed the foreign key to A in B. So either you need to create a bidirectional relationship in the entity model or move the FK to table A.
  2. Although table_b.id references table_a.id, you marked both columns as BIGSERIAL, which will autogenerate both. But only one should be generated and the other should copy the referenced value.
  3. Once you solve the above problems, you need to correctly map the "derived identity". See my answer here which corresponds to your situation.
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the response, unfortunately now I get runtime hibernate exception.
Hard to say without the code showing how you're using it, but: make sure both entity classes implement equals and hashcode and make sure you set both sides of the relationship (a.setB(b) and b.setA(a)) before passing to em.persist. If that doesn't help, please post the code showing how you set up the entities and how you persist them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.