JPA-将对象级联保存到id映射的数据库中



我在将属性不为null的b对象保存到数据库时遇到问题。我将cascade设置为all,所以我希望它也能保存B对象。如果我尝试使用entityManager.merge(a)方法,对象保存时不会出错,但当我在数据库上调用简单的"select"来检查数据库中的记录是否正确时,我会发现table_a和table_b中相应行的ids不同。我应该如何通过管理A对象来修复我的代码B对象是否也将正确保存?

JPA提供商:Hibernate

Darabase:PostgreSQL

我的代码:

@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;
    ...
}

我的表:

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

[在Adam的Michalik回答后编辑]

我的当前代码:

@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;
    ...
}

我的表:

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

现在我在调用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)

这里有几个问题:

  1. 在实体模型中,您有一个从a到B的单向一对一关系。这将把B的外键放在a中。但在DB模式中,您已经把a的外键放进了B中。因此,您需要在实体模型创建一个双向关系,或者把FK移到表a中
  2. 虽然table_b.id引用了table_a.id,但您将这两列都标记为BIGSERIAL,这将自动生成这两列。但是应该只生成一个,另一个应该复制引用的值
  3. 一旦解决了上述问题,就需要正确映射"派生身份"。请看我的答案,它与你的情况相对应

最新更新