本文介绍了Spring Data JPA-为三个表创建@Composite键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从这里扩展我的问题:用三个表定义CompositeKey使用JPA/休眠?.在此示例中,我希望创建复合键以创建PRODUCT_ID,CATEGORY_ID,STOCK_ID的唯一组合.

I am extending my question from here: Define CompositeKey with three tables using JPA/Hibernate?. In this example, I am looking to create Composite key to create unique combination of PRODUCT_ID, CATEGORY_ID, STOCK_ID.

我开发了以下代码,但是不确定如何将记录保存到数据库中.

I developed below code, but not sure on how to save the records into DB.

Stock.java

Stock.java

@Entity
public class Stock implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "STOCK_ID", unique = true, nullable = false)
    private Integer stockId;

    @Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
    private String stockCode;

    @Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
    private String stockName;

    // Owner of the relationship
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "linkPk.stock", cascade = CascadeType.ALL)
    private Set<StockCategoryProductLink> stockCategoryProductLinks = new HashSet<>(0);

    public Stock() {
    }

    public Stock(Integer stockId, String stockCode, String stockName,
            Set<StockCategoryProductLink> stockCategoryProductLinks) {
        super();
        this.stockId = stockId;
        this.stockCode = stockCode;
        this.stockName = stockName;
        this.stockCategoryProductLinks = stockCategoryProductLinks;
    }

    public Integer getStockId() {
        return stockId;
    }

    public void setStockId(Integer stockId) {
        this.stockId = stockId;
    }

    public String getStockCode() {
        return stockCode;
    }

    public void setStockCode(String stockCode) {
        this.stockCode = stockCode;
    }

    public String getStockName() {
        return stockName;
    }

    public void setStockName(String stockName) {
        this.stockName = stockName;
    }

    public Set<StockCategoryProductLink> getStockCategoryProductLinks() {
        return stockCategoryProductLinks;
    }

    public void setStockCategoryProductLinks(Set<StockCategoryProductLink> stockCategoryProductLinks) {
        this.stockCategoryProductLinks = stockCategoryProductLinks;
    }
}

Product.java

Product.java

public class Product implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "PRODUCT_ID", unique = true, nullable = false)
    private Integer productId;

    @Column(name = "PRODUCT_NAME")
    private String productName;

    @Column(name = "PRODUCT_CODE", unique = true, nullable = false, length = 10)
    private String productCode;

    @OneToMany(mappedBy = "linkPk.product", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    private List<StockCategoryProductLink> userDepartmentRoleLinks;

    public Product(String productName, String productCode) {
        this.productName = productName;
        this.productCode = productCode;
    }

    public Integer getProductId() {
        return productId;
    }

    public void setProductId(Integer productId) {
        this.productId = productId;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductCode() {
        return productCode;
    }

    public void setProductCode(String productCode) {
        this.productCode = productCode;
    }

    public List<StockCategoryProductLink> getUserDepartmentRoleLinks() {
        return userDepartmentRoleLinks;
    }

    public void setUserDepartmentRoleLinks(List<StockCategoryProductLink> userDepartmentRoleLinks) {
        this.userDepartmentRoleLinks = userDepartmentRoleLinks;
    }
}

Category.java

Category.java

Entity
@Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "CATEGORY_ID", unique = true, nullable = false)
    private Integer categoryId;

    @Column(name = "NAME", nullable = false, length = 10)
    private String name;

    @Column(name = "[DESC]", nullable = false)
    private String desc;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "linkPk.category")
    private Set<StockCategoryProductLink> stockCategories = new HashSet<>(0);

    public Category() {
    }

    public Category(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public Category(String name, String desc, Set<StockCategoryProductLink> stockCategories) {
        this.name = name;
        this.desc = desc;
        this.stockCategories = stockCategories;
    }

    public Integer getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Integer categoryId) {
        this.categoryId = categoryId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public Set<StockCategoryProductLink> getStockCategories() {
        return stockCategories;
    }

    public void setStockCategories(Set<StockCategoryProductLink> stockCategories) {
        this.stockCategories = stockCategories;
    }

}

StockCategoryProductLink.java

StockCategoryProductLink.java

@Embeddable
public class StockCategoryProductLink implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    private StockCategoryProductLinkId linkPk = new StockCategoryProductLinkId();

    @Transient
    public Stock getStock() {
        return getLinkPk().getStock();
    }

    @Transient
    public Category getCategory() {
        return getLinkPk().getCategory();
    }

    @Transient
    public Product getProduct() {
        return getLinkPk().getProduct();
    }


    public StockCategoryProductLinkId getLinkPk() {
        return linkPk;
    }

    public void setLinkPk(StockCategoryProductLinkId linkPk) {
        this.linkPk = linkPk;
    }
}

StockCategoryProductLinkId.java

StockCategoryProductLinkId.java

@Embeddable
public class StockCategoryProductLinkId {
    @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinColumn(name = "CATEGORY_ID")
    private Category category;

    @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinColumn(name = "STOCK_ID")
    private Stock stock;

    @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    public Stock getStock() {
        return stock;
    }

    public void setStock(Stock stock) {
        this.stock = stock;
    }

    public Product getProduct() {
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }
}

ManyToManyApplication.java

ManyToManyApplication.java

@SpringBootApplication
public class ManyToManyApplication implements CommandLineRunner{

    public static void main(String[] args) {
        SpringApplication.run(ManyToManyApplication.class, args);
    }

    @Autowired
    private CategoryRepository categoryRepository;
    @Autowired
    private StockRepository stockRepository;
    @Autowired
    private ProductRepository productRepository;

    @Override
    public void run(String... args) throws Exception {
        saveDataFirstTime();

    }


    private void saveDataFirstTime() {
        // Category
        Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
        categoryRepository.save(category1);

        // Product
        Product product = new Product("Product-1", "AB");
        productRepository.save(product);

        // Stock
        Stock stock = new Stock();
        stock.setStockCode("7052");
        stock.setStockName("PADINI");

        // StockCategoryProductLink
        StockCategoryProductLink link = new StockCategoryProductLink();
        link.getLinkPk().setCategory(category1);
        link.getLinkPk().setProduct(product);
        link.getLinkPk().setStock(stock);

        stock.getStockCategoryProductLinks().add(link);

        stockRepository.save(stock);
    }
}

错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Use of @OneToMany or @ManyToMany targeting an unmapped class: com.example.entity.Stock.stockCategoryProductLinks[com.example.entity.StockCategoryProductLink]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202) [spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at com.example.ManyToManyApplication.main(ManyToManyApplication.java:20) [classes/:na]
Caused by: org.hibernate.AnnotationException: Use of @OneToMany or @ManyToMany targeting an unmapped class: com.example.entity.Stock.stockCategoryProductLinks[com.example.entity.StockCategoryProductLink]
    at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1274) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:811) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:736) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1696) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1664) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) ~[spring-orm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) ~[spring-orm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) ~[spring-orm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    ... 15 common frames omitted

推荐答案

好,我知道您说的是什么,但我认为这不是您的意思.你说

Well, I know what you said, but I don't think it's what you meant. You said

有助于抽象地考虑这一点.用Chen表示法你说的是

It helps to think about this abstractly. In Chen notation what you said is

但是,这可能不是您的意思.这是有问题的,因为对于每个StockProduct关系,您都需要一个新的Category实体.因此,如果您有一个ETF类别,那么BobsBestETFs产品中的每只股票(实际上是每一个实例化的关系)都将复制它.

However this is probably not what you meant. This is problematic because you will need a new Category entity for every Stock and Product relationship. So, if you have a category of ETF then it will be duplicated for every stock in BobsBestETFs product, in fact for every instantiated relation.

您的意思可能更像是具有Category属性的StockProduct关系,像这样.

What you meant is probably more along the lines of a Stock and Product relationship with a Category attribute, like so.

这将允许许多产品每个都有很多库存,并且为每个产品/库存关系提供特定的类别属性.您将遇到的问题是,您不希望Category成为属性,而希望成为类别的查找表,例如:

This this allows for many products each with many stocks and a specific category attribute for each product/stock relation. The issue you will have with this is that you don't want Category to be an attribute but rather a lookup table of categories, like so:

我想这就是您要寻找的.使用复合ID实施起来应该相当简单,但是您显示的示例似乎有些过时或不清楚.最好找到更好的例子.这就是我对最后一个架构进行建模的方式.

And I think this is what you are looking for. This should be fairly simple to implement with a composite ID but the examples you are showing seem to be somewhat out of date or unclear. Better to find better examples. This is how I would model the last schema.

@Entity
public class Stock {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
@Entity
@Data
public class Product {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
@Entity
public class Category {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
@Entity
@Data
public class StockProduct {
    @EmbeddedId
    private StockProductPk id;

    @ManyToOne
    @MapsId("productId")
    private Product product;
    @ManyToOne
    @MapsId("stockId")
    private Stock stock;

    @ManyToOne
    private Category category;
}
@Embeddable
@Data
public class StockProductPk implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long stockId;
    private Long productId;
}

并举例说明:

private void create() {
    Category catEtf = new Category();
    categoryRepo.save(catEtf);
    Stock s1 = new Stock();
    stockRepo.save(s1);
    Product bobEtfs = new Product();
    productRepo.save(bobEtfs);

    // create a relationship
    StockProduct bs1 = new StockProduct();
    bs1.setId(new StockProductPk());
    bs1.setProduct(bobEtfs);
    bs1.setStock(s1);
    bs1.setCategory(catEtf);
    stockProductRepo.save(bs1);
}
private void read() {
    StockProduct sp1 = new StockProduct();
    Product p1 = new Product();
    p1.setId(1L);
    sp1.setProduct(p1);
    List<StockProduct> bobEtfs = stockProductRepo.findAll(Example.of(sp1, ExampleMatcher.matching()));
    System.out.println(bobEtfs);
}

这篇关于Spring Data JPA-为三个表创建@Composite键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 00:08