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