天天看點

【架構】利用JPA的JOIN FETCH讀取FetchType.LAZY成員前言原理方案

2019.04.07

文章目錄

  • 前言
  • 原理
  • 方案
    • 事務@Transactional
    • JOIN FETCH
      • 場景一:@Query或Native SQL
      • 場景二:JPA Criteria API或Spring Specification API
    • @NamedEntityGraph
    • @FetchProfile

前言

Spring+Hibernate的項目,裡面大量用到

FetchType.LAZY

,懶加載實體成員。新需求開發一旦用到lazy成員,就會報

org.hibernate.LazyInitializationException

的異常。示例如下:

@Entity
@Table(name = "order")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id", referencedColumnName = "id")
    private Product product;
}

@Entity
@Table(name = "product")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @Column
    private String name;
    @Column
    private Float price;
}
           

原理

先看看Hibernate文檔1 2:

A “fetch” join allows associations or collections of values to be initialized along with their parent objects using a single select.

[1]: Hibernate Query HQL

[2]: Hibernate Fetch Strategies

JOIN FETCH中的fetch,是可以在單條select語句中,初始化對象中的關聯或集合。

如示例中

Order

product

成員,它是lazy成員,預設情況下是不會被初始化的,也就是說如果通過

getProduct()

通路成員的時候,就會報

LazyInitializationException

的異常。

方案

利用JPA的JOIN FETCH就可以擷取lazy成員[3][4]。

[3]: Stackoverflow about Join Fetch

[4]: Hibernate Get and Load Difference

事務@Transactional

如果在同一個事務上下文内,是可以擷取到lazy成員的,但在長事務或者多線程的場景下,這種方法就不合适3:

@Transactional
public void runWithinTransaction() {
    Order order = orderService.getOne(1);
    System.out.println(order.getProduct()); 
}
           

[5]: Stackoverflow about Transaction and Thread

JOIN FETCH

join fetch在JPA的sql裡可以很自然地實作,但在Criteria API中得采用

FetchParent#fetch

API實作[6][7]

[6]: FetchParent API

[7]: Stackoverflow about Criteria Join Fetch

場景一:@Query或Native SQL

select o from Order o join fetch o.product p where o.id = :id
           

場景二:JPA Criteria API或Spring Specification API

(root, query, criteriaBuilder) -> {
	        root.fetch("product"); // JOIN FETCH
	        //...
        }
           

@NamedEntityGraph

@NamedEntityGraph(
        name = "order-entity-graph",
        attributeNodes = {
                @NamedAttributeNode("product")
        }
)
public class Order { //... }
           

@NamedEntityGraph

需要與

@EntityGraph

配合使用,上例即為

@EntityGraph(value = "order-entity-graph")

@FetchProfile

留給讀者自行探索

  1. 1 ↩︎
  2. 2 ↩︎
  3. 5 ↩︎