【框架】利用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语句中,初始化对象中的关联或集合。

如示例中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 ↩︎

你可能感兴趣的:(Spring,Java,JPA,Hibernate)