Java Persistence API (JPA)是Java EE技術之一,提供了一種數據持久化的解決方案。在很多實際應用中,需要從多個表中獲取數據並進行聯表查詢,因此JPA聯表查詢是非常重要的。本文將從多個方面進行詳細講解。
一、基本的JPA聯表查詢
在JPA中,實體類對應資料庫中的表,而查詢的結果封裝為實體對象。當需要進行多表查詢時,可以利用JPA的關聯(@ManyToOne,@OneToOne,@OneToMany和@ManyToMany)來定義關聯關係,從而方便進行聯表查詢。
以一個訂單(Order)和顧客(Customer)的關係為例,Order作為主表,Customer作為從表,它們之間是一對多的關係,因此Order需要使用@ManyToOne註解來定義與Customer的關聯:
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_no")
private String orderNo;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
// getter and setter
}
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "customer")
private List<Order> orders;
// getter and setter
}
在上面的實體類中,Order使用了@ManyToOne註解,定義了與Customer的關聯,同時使用@JoinColumn註解指定了外鍵欄位名稱。而Customer使用了@OneToMany註解,定義了與Order的關聯關係,同時指定了mappedBy參數,表示由Order維護關係。
在進行聯表查詢時,只需要使用EntityManager的createQuery方法來創建查詢語句,然後使用select方法指定需要查詢的欄位,使用from方法指定要查詢的主表,使用join方法來指定要關聯的從表,最後使用where方法來添加過濾條件即可:
TypedQuery<Order> query = em.createQuery(
"select o from Order o join o.customer c where c.name = :customerName", Order.class);
query.setParameter("customerName", "Alice");
List<Order> orders = query.getResultList();
上面的查詢語句使用了JOIN語句將Order和Customer表關聯起來,並添加了過濾條件c.name = :customerName,指定只查詢顧客名稱為Alice的訂單信息。
二、JPA的左外聯表查詢
在實際開發中,需要查詢某張主表的所有記錄以及對應的從表信息,但是從表中可能沒有與主表對應的記錄。此時就需要用到左外聯表查詢。
以一個商品(Item)和庫存(Inventory)的關係為例,Inventory中的商品信息不一定全部與Item對應,有可能某些商品還未入庫,因此需要使用左外聯表查詢來獲取所有 Item 對象以及對應的庫存信息:
@Entity
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "price")
private BigDecimal price;
// getter and setter
}
@Entity
public class Inventory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "quantity")
private Integer quantity;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
// getter and setter
}
在上面的實體類中,Inventory使用了@ManyToOne註解來定義與Item的關聯關係。由於默認情況下JPA對於@ManyToOne和@OneToMany關係都使用延遲載入,因此需要在查詢時使用JOIN FETCH語句來強制載入:
TypedQuery<Item> query = em.createQuery(
"select i from Item i left join fetch i.inventories", Item.class);
List<Item> items = query.getResultList();
for (Item item : items) {
System.out.println(item.getName() + ": " + item.getInventories().size());
}
上面的查詢語句使用了LEFT JOIN FETCH語句將Item和Inventory表進行左外聯表查詢,並使用了item.getInventories()方法來獲取所有庫存信息。由於使用了JOIN FETCH語句,因此會立即查詢出所有記錄,而不會像默認情況下那樣使用延遲載入的方式。
三、JPA的多表聯合查詢
在某些情況下,需要從多個表中獲取數據併合並成一個結果集。此時就需要使用到多表聯合查詢。
以一個訂單(Order)、顧客(Customer)和訂單項(OrderItem)之間的關係為例,需查詢出某位顧客購買的所有商品的訂單信息:
@Entity
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "quantity")
private Integer quantity;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
// getter and setter
}
TypedQuery<OrderItem> query = em.createQuery(
"select i from OrderItem i where i.order.customer.name = :customerName", OrderItem.class);
query.setParameter("customerName", "Alice");
List<OrderItem> items = query.getResultList();
for (OrderItem item : items) {
System.out.println(item.getOrder().getOrderNo() + ": " + item.getQuantity());
}
上面的查詢語句使用了 i.order.customer.name = :customerName 來限定只查詢顧客名稱為Alice的訂單項。注意到OrderItem中同時有與Order和Item的關係,因此可以使用 i.order 和 i.item 進行多表聯合查詢。
四、JPA的子查詢
在某些場景下,需要在主查詢內進行子查詢以獲取更精細的數據。此時就需要使用到JPA的子查詢功能。
以一個訂單(Order)和訂單項(OrderItem)的關係為例,需查詢所有訂單中購買Toy商品的信息:
TypedQuery<Order> query = em.createQuery(
"select o from Order o where o.id in (select i.order.id from OrderItem i " +
"where i.item.name = :itemName)", Order.class);
query.setParameter("itemName", "Toy");
List<Order> orders = query.getResultList();
for (Order order : orders) {
System.out.println(order.getOrderNo() + ": " + order.getCustomer().getName());
}
上面的查詢語句使用了”select i.order.id from OrderItem i where i.item.name = :itemName”來獲取所有購買Toy商品的訂單項ID,然後使用”select o from Order o where o.id in (…) “來查詢所有對應的訂單信息。
五、JPA的動態條件查詢
在實際開發中,需要根據用戶的查詢條件來動態構建查詢語句,此時就需要使用到JPA的動態條件查詢。
以一個訂單(Order)的幾個屬性為例,假設用戶輸入了訂單編號、下單時間的開始和結束時間,需要根據用戶輸入來動態構建查詢語句:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> query = cb.createQuery(Order.class);
Root<Order> root = query.from(Order.class);
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(orderNo)) {
predicates.add(cb.equal(root.get("orderNo"), orderNo));
}
if (startTime != null) {
Path<Date> createDate = root.get("createdAt");
predicates.add(cb.greaterThanOrEqualTo(createDate, startTime));
}
if (endTime != null) {
Path<Date> createDate = root.get("createdAt");
predicates.add(cb.lessThanOrEqualTo(createDate, endTime));
}
query.select(root).where(predicates.toArray(new Predicate[]{}));
List<Order> orders = em.createQuery(query).getResultList();
以上代碼中,首先使用EntityManager的getCriteriaBuilder方法創建CriteriaBuilder對象,然後使用CriteriaQuery來構建查詢語句。在構建過程中,需要根據用戶的輸入來動態添加過濾條件,即利用Predicate來實現。最後利用EntityManager的createQuery方法執行查詢操作。
六、總結
JPA聯表查詢是Java EE技術中非常重要的一部分,本文從基本的JPA聯表查詢、左外聯表查詢、多表聯合查詢、子查詢和動態條件查詢五個方面進行了詳細的講解,希望能夠幫助讀者更好地掌握JPA的聯表查詢功能。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/257535.html