[英]Mapping entities with composite foreign keys in JPA
我有一個應用程序,其中所有數據庫表都有一個名為 BRAND 的列,該列位於PRIMARY KEY
。
在客戶表中,我有一個列id
(autoincremnt),它與品牌一起構成一個復合鍵,映射到 Java 實體如下:
@Entity
@Table(name = "CUSTOMERS")
public class CustomerEntity implements Serializable {
@EmbeddedId
private CustomerId id;
//getters and setters and other attributes hidden
}
@Embeddable
public class CustomerId implements Serializable {
@Column(name = "BRAND")
private String brand;
@Column(name = "ID_CUSTOMER")
private String idCustomer;
//getters and setters hidden
}
問題在於將 Customer 實體映射為 Order 實體的外鍵。
@Entity
@Table(name = "ORDERS")
public class OrderEntity implements Serializable {
@EmbeddedId
private OrderId id;
@Column(name = "STATUS")
private Integer status;
@Column(name = "DELIVERY_DATE")
private LocalDateTime deliveryDate;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumns(value = {
@JoinColumn(name = "ID_CUSTOMER", referencedColumnName = "ID_CUSTOMER", updatable = false, insertable = false),
@JoinColumn(name = "BRAND", referencedColumnName = "BRAND", updatable = false, insertable = false)})
private CustomerEntity customer;
}
@Embeddable
public class OrderId implements Serializable {
@Column(name = "ID_ORDER")
private String idOrder;
@Column(name = "BRAND")
private String brand;
}
通過這種方式,我可以讀取訂單並通過調用orderRepository.findAll()
將客戶聚集在一起。
但是,當我保存新訂單,將現有客戶傳遞給訂單實體時,客戶不會通過調用orderRepository.save(order)
保存在orderRepository.save(order)
。
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(value = {
@JoinColumn(name = "ID_CUSTOMER", referencedColumnName = "ID_CUSTOMER"),
@JoinColumn(name = "BRAND", referencedColumnName = "BRAND")})
private CustomerEntity customer;
我收到以下異常:
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 javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
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.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
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.SpringApplication.run(SpringApplication.java:1162)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151)
at br.com.dasa.DasaIntegracaoUraApplication.main(DasaIntegracaoUraApplication.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:954)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:882)
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)
... 21 common frames omitted
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: br.com.dev.orm.entity.OrderEntity column: brand (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:830)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:848)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:870)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:605)
at org.hibernate.mapping.RootClass.validate(RootClass.java:265)
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:329)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:443)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879)
... 27 common frames omitted
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(value = {
@JoinColumn(
name = "ID_CUSTOMER",
referencedColumnName = "ID_CUSTOMER"
),
@JoinColumn(
name = "BRAND",
referencedColumnName = "BRAND",
insertable = false,
updatable = false
)
})
private CustomerEntity customer
我收到以下異常:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:47)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:283)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:173)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:128)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103)
Caused by: 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 org.hibernate.AnnotationException: Mixing insertable and non insertable columns in a property is not allowed: br.com.dasa.orm.entity.RelacaoUraExameEspecialidadeEntity.especialidade
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.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
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.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
... 25 common frames omitted
Caused by: org.hibernate.AnnotationException: Mixing insertable and non insertable columns in a property is not allowed: br.com.dev.orm.entity.OrderEntity.customer
at org.hibernate.cfg.Ejb3Column.checkPropertyConsistency(Ejb3Column.java:710)
at org.hibernate.cfg.AnnotationBinder.bindManyToOne(AnnotationBinder.java:2937)
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1763)
at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:911)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:738)
at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:245)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265)
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)
... 41 common frames omitted
BRAND | ID_ORDER | ID_CUSTOMER | STATUS | DELIVERY_DATE
在 Order 類中映射 Customer(復合外鍵)的正確方法是什么?
我看到多個問題:
為什么idCustomer
屬性是String
。 如果它是AUTO_INCREMENTED
列,它不應該是Long
或Integer
嗎?
您沒有顯示將客戶信息保存到您的訂單中的代碼,但問題是您使用了updatable = false, insertable = false
只應在您復制映射時使用,例如當客戶 ID 包含在訂購PK。 只需將映射更改為:
@ManyToOne(fetch = FetchType.LAZY) @JoinColumns(value = { @JoinColumn( name = "ID_CUSTOMER", referencedColumnName = "ID_CUSTOMER" ), @JoinColumn( name = "BRAND", referencedColumnName = "BRAND", insertable = false, updatable = false ) }) private CustomerEntity customer;
,一切都應該正常工作。
customer
關聯中刪除 EAGER 獲取,因為它可能是性能問題的主要原因並導致 N+1 查詢問題。我遇到了類似的問題並使用了以下解決方案。 正如您已經注意到的,不允許在屬性中混合可插入和不可插入的列。 因此,我將兩個@JoinColumn
注釋都@JoinColumn
為不可插入,並包含了一個僅映射要保存的列的附加屬性。 例如:
@Entity
@Table(name = "ORDERS")
public class OrderEntity implements Serializable {
@EmbeddedId
private OrderId id;
// ...
@ManyToOne
@JoinColumns(value = {
@JoinColumn(name = "ID_CUSTOMER", referencedColumnName = "ID_CUSTOMER", updatable = false, insertable = false),
@JoinColumn(name = "BRAND", referencedColumnName = "BRAND", updatable = false, insertable = false)})
private CustomerEntity customer;
// This additional property only exists to allow writing to ID_CUSTOMER
@Column(name = "ID_CUSTOMER")
private String idCustomer;
// The customer setter also sets the idCustomer, for consistency
public void setCustumer(CustomerEntity customer) {
this.customer = customer;
this.idCustomer = customer != null ? customer.getId().getIdCustumer() : null;
}
}
這感覺就像一個黑客,但這是我發現的唯一可行的解決方案。
另外,請注意,我在setCustumer
強制執行customer
和idCustomer
之間的一致性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.