jpa和hibernate
每个JPA实体都有一个主键,但是某些实体具有多个值作为其主键。 在这种情况下,您需要使用组合键。 该Java技巧向您介绍如何在JPA和Hibernate中使用复合键。
请注意,我假设您已经通过JPA和Hibernate引入了Java持久性,包括如何使用主键为实体和关系建模。 如果您不熟悉这些概念,请参阅教程JPA和Hibernate的Java持久性 。
当您需要组合键时
考虑一个产品定价表,该表根据地区名称和产品ID来存储产品价格。 在这种情况下,您的表可能包含具有相同产品ID的多个行,但每个行都与一个不同的区域相关联。 您需要产品ID和地区名称,以唯一地区分不同地区的产品价格。
我们将使用两个JPA构造来解决此问题:
- Embeddable Object :我们将创建一个新类
ProductPriceId
,该类带有@Embeddable
批注,同时包含产品ID和区域名称,该区域名称代表产品价格的主键。 - 嵌入式ID :我们将创建
ProductPrice
实体,并使用@EmbeddableId
批注将ProductPriceId
引用为其id
。
首先,请研究如下所示的ProductPriceId
和ProductPrice
类的源代码。
清单1. ProductPriceId.java
package com.geekcap.javaworld.jpa.model;
import javax.persistence.Embeddable;
import java.io.Serializable;
@Embeddable
public class ProductPriceId implements Serializable {
private String region;
private Integer productId;
public ProductPriceId() {
}
public ProductPriceId(String region, Integer productId) {
this.region = region;
this.productId = productId;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
@Override
public String toString() {
return "ProductPriceId{" +
"region='" + region + '\'' +
", productId=" + productId +
'}';
}
}
清单2. ProductPrice.java
package com.geekcap.javaworld.jpa.model;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "PRODUCT_PRICE")
public class ProductPrice {
@EmbeddedId
private ProductPriceId id;
private Double price;
public ProductPrice() {
}
public ProductPrice(ProductPriceId id, Double price) {
this.id = id;
this.price = price;
}
public ProductPriceId getId() {
return id;
}
public void setId(ProductPriceId id) {
this.id = id;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "ProductPrice{" +
"id=" + id +
", price=" + price +
'}';
}
}
ProductPriceId
类(清单1)是一个简单的Java类,具有两个成员变量: region
和productId
。 它带有@Embeddable
注释。
ProductPrice
类(清单2)是一个JPA实体,它映射到“ PRODUCT_PRICE”表并定义了ProductPriceId
类型的id
字段。 ProductPriceId
字段类型使用@EmbeddedId
注释进行注释。
具有复合键的示例应用程序
清单3显示了示例应用程序的源代码,该示例应用程序创建四个不同的产品价格,通过其ProductPriceId
执行对产品的查询,查询所有产品价格,然后转储“ PRODUCT_PRICE”表的内容,以便我们了解如何Hibernate代表数据。
清单3. JpaExampleCompositeKey.java
package com.geekcap.javaworld.jpa;
import com.geekcap.javaworld.jpa.model.ProductPrice;
import com.geekcap.javaworld.jpa.model.ProductPriceId;
import org.hibernate.Session;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaExampleCompositeKey {
public static void main(String[] args) {
// Create our entity manager
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("SuperHeroes");
EntityManager entityManager = entityManagerFactory.createEntityManager();
// Create a product price
ProductPrice productPrice1 = new ProductPrice(new ProductPriceId("EAST", 1), 500.0d);
ProductPrice productPrice2 = new ProductPrice(new ProductPriceId("WEST", 1), 400.0d);
ProductPrice productPrice3 = new ProductPrice(new ProductPriceId("EAST", 2), 200.0d);
ProductPrice productPrice4 = new ProductPrice(new ProductPriceId("WEST", 2), 150.0d);
try {
// Save the product prices to the database
entityManager.getTransaction().begin();
entityManager.persist(productPrice1);
entityManager.persist(productPrice2);
entityManager.persist(productPrice3);
entityManager.persist(productPrice4);
entityManager.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
}
// Query for product1 by its ID
ProductPrice productPrice = entityManager.find(ProductPrice.class, new ProductPriceId("EAST", 1));
System.out.println(productPrice);
// Find all product prices
List<ProductPrice> productPrices = entityManager.createQuery("from ProductPrice").getResultList();
System.out.println("\nAll Product Prices:");
productPrices.forEach(System.out::println);
// DEBUG, dump our tables
entityManager.unwrap(Session.class).doWork(connection ->
JdbcUtils.dumpTables(connection, "PRODUCT_PRICE"));
// Close the entity manager and associated factory
entityManager.close();
entityManagerFactory.close();
}
}
此应用程序创建一个新的EntityManagerFactory
,该实体引用“ 包含JPA和Hibernate的Java持久性,第2部分 ”中的SuperHeroes
持久性单元,其中包含ProductPrice
实体。 然后,它从该持久性单元创建EntityManager
。 它创建四个产品价格:两个分别针对产品1和两个针对产品2,但位于两个不同的区域:“东”和“西”。 然后,使用EntityManager::persist
方法将其EntityManager::persist
到数据库中。
接下来,它使用EntityManager::find
方法查询ProductPrice
,该方法需要检索的实体的类名称及其主键。 因为我们使用的是复合键,所以我们向它传递了一个新的ProductPriceId
实例,该实例的区域名称设置为“ EAST”,产品ID设置为1。这将产生以下输出:
ProductPrice{id=ProductPriceId{region='EAST', productId=1}, price=500.0}
用组合键查询
使用复合键查询实体正是您所期望的:只需将主键传递给find()
方法。 唯一的区别是,在这种情况下,我们没有传递String
或Integer
,而是传递ProductPriceId
。
然后,示例应用程序使用JPQL查询“ from ProductPrice
”查询所有产品价格,这将产生以下输出:
ProductPrice{id=ProductPriceId{region='EAST', productId=1}, price=500.0}
ProductPrice{id=ProductPriceId{region='WEST', productId=1}, price=400.0}
ProductPrice{id=ProductPriceId{region='EAST', productId=2}, price=200.0}
ProductPrice{id=ProductPriceId{region='WEST', productId=2}, price=150.0}
我们将看到持久化到数据库中的所有四个产品价格。 “ PRODUCT_PRICE”表包含来自我们的JdbcUtils::dumpTables
方法调用的以下数据:
Table: PRODUCT_PRICE
{PRODUCTID: 1, REGION: EAST, PRICE: 500.0},
{PRODUCTID: 1, REGION: WEST, PRICE: 400.0},
{PRODUCTID: 2, REGION: EAST, PRICE: 200.0},
{PRODUCTID: 2, REGION: WEST, PRICE: 150.0},
将ProductPriceId
中的字段以及我们的附加ProductPrice
属性: price
直接写入“ PRODUCT_PRICE”表中的数据库列。 Hibernate处理将字段填充到适当的类中。
Java持久性与Spring Data
想继续吗? 注意Java持久性系列中的下一个教程,介绍Spring Data作为Hibernate for JPA的替代方法。
这个故事“ JPA和Hibernate中的复合键”最初是由JavaWorld发布的 。
翻译自: https://www.infoworld.com/article/3391019/composite-keys-in-jpa-and-hibernate.html
jpa和hibernate