java - 如何使用@EmbeddedId 在两个表之间进行映射?

标签 java spring postgresql hibernate jpa

所以我这里有一个看起来像这样的图表,
可以在此 Answer Here 中找到.

 +---------------+     +-------------------+
 | 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_VALUESPRODUCT_VARIANTS有复合 Id(主键),我使用 @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_VALUESPRODUCT_VARIANTSVARIANT_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,
当我添加一个 @EmbddedVariantValueId它生成两列,我找不到任何方法来排除一个值并使用另一个值,
所以生成的表现在看起来像这样 //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
我尝试了克里斯蒂安贝科夫的方法
@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
}

关于java - 如何使用@EmbeddedId 在两个表之间进行映射?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64993785/

相关文章:

java - 从 fragment 创建新的列表适配器。知道如何从 Activity 而不是 fragment 中做到这一点

java - 我可以通过扫描 spring 配置文件中的 beans 来动态创建一个列表吗?

java - Autowiring 优先级

postgresql - 查询按月查找员 worker 数

c# - 无法立即在 Npgsql 中收到多个通知

java - 抽象类中的公共(public)字段?

java - FileChannel ByteBuffer 和哈希文件

java - 检查是否可以插入 MySQL 数据库

node.js - Sequelize 中不重音

java - 如何以线程安全的方式将递增的用户 ID 分配给新用户?