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;
}
原理
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
留給讀者自行探索