如何通过JOIN FETCH避免Hibernate/JPA的LazyInitializationException?

19-02-18 banq
              

如果你从来没有遇到过著名LazyInitializationException, 那么你实际上没有真正使用Hibernate过:),但是,如果你遇到过,你是将LAZY懒加载切换到AGER立即加载,那么这里建议对你有用。

通常情况下,当我们遇到一个LazyInitializationException,我们倾向于修改关系抓取类型,将LAZY切换到EAGER,但这很不好!这是一种代码坏味道。避免此异常的最佳方法是依赖JOIN FETCH+ DTO(如果需要)。这里的应用程序是JOIN FETCH没有DTO 的示例(它在单个SELECT查询中获取实体)。点击#DTO可以获得更多DTO案例。

关键点:

  • 定义两个相关实体(例如,Category和Product之间有一对多的lazy双向关系)

@Entity
public class Category implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "category_name")
    private String cname;

    @OneToMany(cascade = CascadeType.ALL, 
            mappedBy = "category", orphanRemoval = true)
    private List<Product> products = new ArrayList<>();

@Entity
public class Product implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "product_name")
    private String pname;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;

  • 编写一个JPQL JOIN FETCH来获取包含Product产品的类别Category

@Repository
@Transactional(readOnly = true)
public interface CategoryRepository extends JpaRepository<Category, Long> {

    @Query(value = "SELECT c FROM Category c JOIN FETCH c.products WHERE c.cname = ?1")
    Category categoryIncludingProducts(String cname);
}

  • 编写一个JPQL JOIN FETCH来获取包括类别Category在内的所有产品Product

@Repository
@Transactional(readOnly = true)
public interface ProductRepository extends JpaRepository<Product, Long> {  
    
    @Query(value = "SELECT p FROM Product p JOIN FETCH p.category")
    List<Product> productsIncludingCategory();
}

输出结果:

源代码可以在这里找到