问题描述
我从这里扩展我的问题:用三个表定义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
但是,这可能不是您的意思.这是有问题的,因为对于每个Stock
和Product
关系,您都需要一个新的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
属性的Stock
和Product
关系,像这样.
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键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!