【框架】利用JPA的JOIN FETCH讀取FetchType.LAZY成員

2019.04.07

前言

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語句中,初始化對象中的關聯或集合。

如示例中Orderproduct成員,它是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 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章