@EmbeddedId로 두 테이블을 매핑하는 방법은 무엇입니까?
그래서 제가 여기있는 것은이 답변 에서 찾을 수있는 이와 같은 다이어그램입니다 .
+---------------+ +-------------------+
| PRODUCTS |-----< PRODUCT_VARIANTS |
+---------------+ +-------------------+
| #product_id | | #product_id |
| product_name | | #variant_id |
+---------------+ | sku_id |
| +-------------------+
| |
+--------^--------+ +--------^--------+
| PRODUCT_OPTIONS |-----< VARIANT_VALUES |
+-----------------+ +-----------------+
| #product_id | | #product_id |
| #option_id | | #variant_id |
+--------v--------+ | #option_id |
| | value_id |
+-----------------+ +--------v--------+
| OPTIONS | |
+-----------------+ |
| #option_id | |
| option_name | |
+-----------------+ |
| |
+-------^-------+ |
| OPTION_VALUES |---------------+
+---------------+
| #option_id |
| #value_id |
| value_name |
+---------------+
테이블을 제외하고 내 Spring 앱에서 전체 다이어그램을 구현했습니다. VARIANT_VALUES
문제는 디자인이 테이블 제안이다 PRODUCT_OPTIONS
, OPTION_VALUES
그리고 PRODUCT_VARIANTS
내가 사용하여 구현 화합물 IDS (기본 키),이 @EmbeddedId
중 세 가지에있다.
예를 들면 다음과 같습니다. //PRODUCT_VARIANTS
@Data
@NoArgsConstructor
@Entity
@Table(name = "product_variants")
public class ProductVariant {
@EmbeddedId
private ProductVariantId productVariantId;
@NotNull
private Double price;
@MapsId("productId")
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
//other properties
}
그리고 //ProductVariantId
@Data
@Embeddable
public class ProductVariantId implements Serializable {
@EqualsAndHashCode.Include
private Long productVariantId;
@EqualsAndHashCode.Include
private Long productId;
}
이제 세 테이블 ( PRODUCT_OPTIONS
, OPTION_VALUES
및 PRODUCT_VARIANTS
)을 다시 매핑해야합니다.VARIANT_VALUES
그래서 저는이 접근법을 시도했습니다 //VariantValue
@Data
@NoArgsConstructor
@Entity
@Table(name = "variant_values")
public class VariantValue {
@EmbeddedId
private VariantValueId variantValueId;
@MapsId("productVariantId")
@ManyToOne(fetch = FetchType.LAZY)
private ProductVariant productVariant;
@MapsId("productOptionId")
@ManyToOne(fetch = FetchType.LAZY)
private ProductOption productOption;
@MapsId("optionValueId")
@ManyToOne(fetch = FetchType.LAZY)
private OptionValue optionValue;
}
//VariantValueId
@Embeddable
@Data
public class VariantValueId implements Serializable {
@EqualsAndHashCode.Include
@Embedded
private ProductVariantId productVariantId;
@EqualsAndHashCode.Include
@Embedded
private ProductOptionId productOptionId;
@EqualsAndHashCode.Include
@Embedded
private OptionValueId optionValueId;
}
하지만 결국 세 테이블 모두에서 모든 ID가있는 테이블을 만들었습니다.
내가 추가하는 경우 @Embdded
에 VariantValueId
는 두 개의 열을 생성하고 내가 하나 개의 값을 제외하고 다른를 사용할 수있는 방법을 찾을 수 없습니다,
이제 생성 된 테이블은 다음과 같습니다. //variant_values
product_option_option_option_id [PK]
product_option_ptoduct_product_id [PK]
product_variant_ptoduct_product_id [PK]
product_variant_ptoduct_variant_id [PK]
product_value_option_option_id [PK]
product_value_option_value_id [PK]
그래서 어떻게 난 단지하려면이를 구현할 수 product_id [PK]
, variant_id [PK]
, option_id [PK]
그리고 value_id
디자인에서 알 수 있듯이?
편집 1
나는 Christian Beikov의 접근 방식을 시도했습니다
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "product_variants")
public class ProductVariant extends UserDateAudit {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "productVariantId", column = @Column(name = "variant_id")),
@AttributeOverride(name = "productId", column = @Column(name = "product_id"))
})
private ProductVariantId productVariantId;
@NotNull
private Double price;
//many to many with product entity, option entity and optionValue entity
@MapsId("productId")
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
@OneToMany(mappedBy = "productVariant", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<VariantValue> variantValues;
}
@Data
@Embeddable
public class ProductVariantId implements Serializable {
private Long productVariantId;
private Long productId;
}
@Data
@NoArgsConstructor
@Entity
@Table(name = "variant_values")
public class VariantValue {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "productVariantId", column = @Column(name = "variant_id")),
@AttributeOverride(name = "productId", column = @Column(name = "product_id"))
@AttributeOverride(name = "optionId", column = @Column(name = "option_id")),
@AttributeOverride(name = "valueId", column = @Column(name = "value_id"))
})
private VariantValueId variantValueId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "variant_id", referencedColumnName = "variant_id", insertable = false, updatable = false),
@JoinColumn(name = "product_id", referencedColumnName = "product_id", insertable = false, updatable = false)
})
private ProductVariant productVariant;
//other mappings
다음과 같은 오류가 발생했습니다
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is org.hibernate.MappingException: Unable to find column with logical name: product_id in product_variants
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:691) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:197) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1206) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:571) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:925) ~[spring-context-5.3.1.jar:5.3.1]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588) ~[spring-context-5.3.1.jar:5.3.1]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.0.jar:2.4.0]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767) ~[spring-boot-2.4.0.jar:2.4.0]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.4.0.jar:2.4.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426) ~[spring-boot-2.4.0.jar:2.4.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:326) ~[spring-boot-2.4.0.jar:2.4.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309) ~[spring-boot-2.4.0.jar:2.4.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298) ~[spring-boot-2.4.0.jar:2.4.0]
at com.ecommerce.ECommerceApplication.main(ECommerceApplication.java:10) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.4.0.jar:2.4.0]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is org.hibernate.MappingException: Unable to find column with logical name: product_id in product_variants
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:609) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330) ~[spring-beans-5.3.1.jar:5.3.1]
... 27 common frames omitted
Caused by: org.hibernate.MappingException: Unable to find column with logical name: product_id in product_variants
at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:854) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:255) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:101) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1823) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1767) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1655) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:286) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1224) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1255) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) ~[spring-orm-5.3.1.jar:5.3.1]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.3.1.jar:5.3.1]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-5.3.1.jar:5.3.1]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
답변
대신 다음과 같이 시도하십시오.
@Data
@NoArgsConstructor
@Entity
@Table(name = "product_variants")
public class ProductVariant {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "productVariantId", column = @Column(name = "variant_id")),
@AttributeOverride(name = "productId", column = @Column(name = "product_id"))
})
private ProductVariantId productVariantId;
@NotNull
private Double price;
@MapsId("productId")
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
//other properties
}
@Data
@Embeddable
public class ProductVariantId implements Serializable {
@EqualsAndHashCode.Include
private Long productVariantId;
@EqualsAndHashCode.Include
private Long productId;
}
@Data
@NoArgsConstructor
@Entity
@Table(name = "variant_values")
public class VariantValue {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "productVariantId", column = @Column(name = "variant_id")),
@AttributeOverride(name = "productId", column = @Column(name = "product_id")),
@AttributeOverride(name = "optionId", column = @Column(name = "option_id")),
@AttributeOverride(name = "valueId", column = @Column(name = "value_id"))
})
private VariantValueId variantValueId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "variant_id", referencedColumnName = "variant_id", insertable = false, updatable = false),
@JoinColumn(name = "product_id", referencedColumnName = "product_id", insertable = false, updatable = false)
})
private ProductVariant productVariant;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "option_id", referencedColumnName = "option_id", insertable = false, updatable = false),
@JoinColumn(name = "product_id", referencedColumnName = "product_id", insertable = false, updatable = false)
})
private ProductOption productOption;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "option_id", referencedColumnName = "option_id", insertable = false, updatable = false),
@JoinColumn(name = "value_id", referencedColumnName = "value_id", insertable = false, updatable = false)
})
private OptionValue optionValue;
}
@Embeddable
@Data
public class VariantValueId implements Serializable {
@EqualsAndHashCode.Include
private Long productVariantId;
@EqualsAndHashCode.Include
private Long productId;
@EqualsAndHashCode.Include
private Long optionId;
@EqualsAndHashCode.Include
private Long valueId;
}
편집하다:
다음을 시도하십시오.
@Data
@NoArgsConstructor
@Entity
@Table(name = "product_variants")
public class ProductVariant {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "productVariantId", column = @Column(name = "variant_id")),
@AttributeOverride(name = "productId", column = @Column(name = "product_id"))
})
private ProductVariantId productVariantId;
@NotNull
private Double price;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColum(name = "product_id", insertable = false, updatable = false)
private Product product;
//other properties
}
@AttributeOverride 주석을 사용하여 JPA가 열에 포함 된 ID의 속성을 매핑하도록 할 수 있습니다.
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "productVariantId", column = @Column(name = "variant_id")),
@AttributeOverride(name = "productId", column = @Column(name = "product_id"))
})
private ProductVariantId productVariantId;
그리고 VariantValueId 클래스의 ProductOptionId 및 OptionValueId에 대해 동일하게 수행하십시오.