【问题标题】:how to map between two tables with @EmbeddedId s?如何使用 @EmbeddedId 在两个表之间进行映射?
【发布时间】:2021-03-07 15:58:31
【问题描述】:

所以我这里有一个看起来像这样的图表, 可以在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_OPTIONSOPTION_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;
}

现在我需要用VARIANT_VALUES 再次映射这三个表(PRODUCT_OPTIONSOPTION_VALUESPRODUCT_VARIANTS

所以我尝试了这种方法//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

我尝试了克里斯蒂安·贝科夫的方法

@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] 

【问题讨论】:

    标签: java spring postgresql hibernate jpa


    【解决方案1】:

    试试这样的:

    @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
    }
    

    【讨论】:

    • 非常感谢,我尝试了您的方法,但出现异常,我更新了问题以包含堆栈跟踪。
    • 我之前遇到过这个错误,解决方案是将embeddedId本身添加到“VariantValueId”中,所以我不确定出了什么问题,请在您想查看代码中的其他内容时告诉我来更新我的答案
    • 非常感谢。真的是这样,对我帮助很大,谢谢。
    【解决方案2】:

    您可以强制 JPA 使用 @AttributeOverride 注释在您的列中映射嵌入 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 执行相同操作。

    【讨论】:

    • 谢谢,但这仍然会创建完全相同的架构(以及相同的名称),我的问题不是创建架构的列的命名,我的问题是它确实创建了一些额外的重复列,我不打算创建。
    猜你喜欢
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-05
    • 1970-01-01
    • 2015-02-12
    • 2015-06-11
    相关资源
    最近更新 更多