*发布后编辑现已附加到帖子末尾。
在撰写此问题时,我遇到了一些文档,这些文档在应用时会产生不同的错误。我仍然觉得以下信息仍然与旁观者有关。有关修改,请参阅下面的预发布编辑。
我想在 JPA 2.1 :
中实现以下双向关系我目前的尝试是这样的:
public class A implements Serializable {
private long aId;
private AMore aMore;
private String foo;
@Id
@Column(name = "a_id", nullable = false)
@GeneratedValue(/*...*/)
public long getAId() { return aId; }
@OneToOne(cascade = CascadeType.ALL) // this is owning side
public AMore getAMore() { return aMore; }
public void setAMore(AMore aMore) {
this.aMore = aMore;
this.aMore.setA(this);
}
// set aId
// set/get foo;
}
public class AMore implements Serializable {
private A a;
private long version = 1L;
private String bar;
@Id
@OneToOne(optional = false, mappedBy = "aMore") // this is non-owning-side
@JoinColumn(name="a_id", referencedColumnName = "a_id")
public A getA() { return a; }
@Version
@Column(name = "A_MORE_VER")
public long getVersion() {
return version; // not sure if relevant but thought i'd add this incase.
}
// set A, version
// set/get bar
}
尝试创建EntityManagerFactory失败,出现此异常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:856)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134)
at com.example.Application.main(Application.java:21)
Caused by: java.lang.NullPointerException: null
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:599)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:101)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1786)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1730)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1617)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:278)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:370)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:359)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 14 common frames omitted
深入研究代码,我可以看到对bindFK
的内部调用要求referencedIdentity
(AMore
)包含一个标识符(通过referencedIdentity.getIdentifier()
获得,返回null ,导致错误:
// TableBinder.java ...
else {
idColumns = referencedEntity.getIdentifier().getColumnIterator();
}
// ...
所以我在阅读后编辑了代码并产生了一个不同的错误,所以我想:上述信息不会创建一个新问题,而是希望仍能与求职者相关。
在仔细阅读documentation on one to one with primary and foreign key relationship后,我将aMore类更改为:
public class AMore implements Serializable {
private long aId;
private A a;
private long version = 1L;
private String bar;
@Id
@Column(name = "a_id")
private long getAId() { return aId; }
// removed Id from here and placed on above long property.
@OneToOne(optional = false, mappedBy = "aMore") // this is non-owning-side
@PrimaryKeyJoinColumn(name="a_id", referencedColumnName = "a_id") //CHANGED FROM JoinColumn -> PrimaryKeyJoinColumn
public A getA() { return a; }
@Version
@Column(name = "A_MORE_VER")
public long getVersion() {
return version; // not sure if relevant but thought i'd add this incase.
}
// set A
// set/get bar, version
}
现在我的hibernate查询通过其foo字段获取A在查询中包含不正确的列名:
select a0_.a_id as a_id1_1_
a0_.a_more_a_id as a_f2_1_, -- as well as being a non-existing field, this should not even be included in the query to fetch only the A Entity info. (the relationship is a `FetchType.LAZY`)
a0_.foo as foo3_1_
from MYSCHEMA.A as a0_
where a0_.foo = ? -- bound at runtime, e.g. 'spam'
我感觉这是由于aMore在A侧的映射方式。
任何指针?
同时应用@JBNizet和@ XtremeBaumer的建议,并且还将id从基元更改为包装类型[Long
],我遇到了Id生成问题。
如果我创建两个实体的实例以便同时保留,则aId
上的AMore
未填充我的预期并给出以下错误:
Caused by: org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): com.example.AMore
我认为因为A
和AMore
的ID都为空,需要分配给A
的生成ID,所以A
需要持久化,抓取并将id分配给AMore
。
我的新映射如下所示:
public class A implements Serializable {
private Long aId;
private AMore aMore;
private String foo;
@Id
@Column(name = "a_id", nullable = false)
@GeneratedValue(/*...*/)
public Long getAId() { return aId; }
@OneToOne(cascade = CascadeType.ALL, mappedBy = "a")
public CourseOffering getAMore() { return aMore; }
public void setAMore(AMore aMore) {
this.aMore = aMore;
this.aMore.setA(this);
}
// set aId
// set/get foo;
}
public class AMore implements Serializable {
private Long aId;
private A a;
private long version = 1L;
private String bar;
@Id
@Column(name = "a_id")
private Long getAId() { return aId; }
@OneToOne(optional = false, fetch = FetchType.LAZY)
@MapsId
public CourseOffering getA() { return a; }
// set A, aId
// set/get bar
}
答案 0 :(得分:0)
我发现了这个link并尝试在你的课程中使用它:
public class AMore implements Serializable {
@Id
private long aId;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
private A a;
private long version = 1L;
private String bar;
public A getA() {
return a;
}
}
public class A implements Serializable {
@Id
@GeneratedValue
private long aId;
@OneToOne(mappedBy = "a", cascade = CascafeType.ALL, optional = false)
private AMore aMore;
private String foo;
public long getAId() {
return aId;
}
public AMore getAMore() {
return aMore;
}
public void setAMore(AMore aMore) {
this.aMore = aMore;
this.aMore.setA(this);
}
}
也许这对你有用
答案 1 :(得分:0)
感谢@JBNizet和@XtremeBaumer的贡献。最终解决方案中需要注意的一些事项:
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new Certificate(certPath: Configuration["Tokens:Certificate"], isValid: false).SecurityKey,
ValidateLifetime = true,
ValidateIssuer = true,
ValidateAudience = true,
ClockSkew = TimeSpan.Zero
},
});
属性应该是两个实体的包装类(aId
)而不是原始类(Long
),以便JPA提供程序识别需要使用long
@GenerationValue
的{{1}}默认值是有效ID,因此当在一个会话中保留多个新实体时,它会查找具有0
Id和错误的实体。
将long
注释放在0
关系上方,以便通知JPA提供商@MapsId
字段映射到关系实体的@OneToOne
字段。
请注意,这会自动应用于我们的案例,因为我们只有一个ID字段,而不是使用
@Id
或@Id
的复合ID。有关如何使用父实体的主键映射组合键的信息,请参阅@MapsId
上的文档。
@IdClass
注释上的mappedBy参数应该存在于父级的引用端,因为子实体“拥有”对其父级的id引用。
确保toString和equals / hashcode不包含反向关系深度复制值,以避免无限循环场景。
最终解决方案是编辑@ XtremeBaumer的解决方案:
@EmbeddedId