Home > Software design >  Spring Data JPA - Lazy loading and @Fetch( FetchMode.JOIN)
Spring Data JPA - Lazy loading and @Fetch( FetchMode.JOIN)

Time:09-27

2 Entity ProductMaster and Category

@Entity
@Table(name = "product_master")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@NamedQuery(name = "ProductMaster.findAll", query = "SELECT p FROM ProductMaster p")
public class ProductMaster implements Serializable {
    private static final long serialVersionUID = 1L;

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

    private String description;

//Many Product will have one categoary
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="category_id")
    private Category category;
    
//get and set fn
}

@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@Table(name = "category")
public class Category {

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

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

    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
    @Fetch( FetchMode.JOIN)
    private Set<ProductMaster> products = new HashSet<ProductMaster>();

//get and set fn
}

while fetchAll in JPA repository of Product. Join is performed and its expected

Hibernate: select productmas0_.id as id1_1_, productmas0_.category_id as categor11_1_, productmas0_.created_by as created_2_1_, productmas0_.created_dt as created_3_1_, productmas0_.description as descript4_1_, productmas0_.image as image5_1_, productmas0_.is_favorite as is_favor6_1_, productmas0_.price as price7_1_, productmas0_.title as title8_1_, productmas0_.updated_by as updated_9_1_, productmas0_.updated_dt as updated10_1_ from product_master productmas0_ limit ?

Hibernate: select category0_.id as id1_0_0_, category0_.category_name as category2_0_0_, category0_.created_by as created_3_0_0_, category0_.created_dt as created_4_0_0_, category0_.category_desc as category5_0_0_, category0_.updated_by as updated_6_0_0_, category0_.updated_dt as updated_7_0_0_, products1_.category_id as categor11_1_1_, products1_.id as id1_1_1_, products1_.id as id1_1_2_, products1_.category_id as categor11_1_2_, products1_.created_by as created_2_1_2_, products1_.created_dt as created_3_1_2_, products1_.description as descript4_1_2_, products1_.image as image5_1_2_, products1_.is_favorite as is_favor6_1_2_, products1_.price as price7_1_2_, products1_.title as title8_1_2_, products1_.updated_by as updated_9_1_2_, products1_.updated_dt as updated10_1_2_ from category category0_ left outer join product_master products1_ on category0_.id=products1_.category_id where category0_.id=?

but while fetchAll in JPA repository of category multiple query for product are fired. I want lazy loading behavior here for product while fetch all in Category

select
    category0_.id as id1_0_,
    category0_.category_name as category2_0_,
    category0_.created_by as created_3_0_,
    category0_.created_dt as created_4_0_,
    category0_.category_desc as category5_0_,
    category0_.updated_by as updated_6_0_,
    category0_.updated_dt as updated_7_0_ 
from
    category category0_ Hibernate: 
select
    products0_.category_id as categor11_1_0_,
    products0_.id as id1_1_0_,
    products0_.id as id1_1_1_,
    products0_.category_id as categor11_1_1_,
    products0_.created_by as created_2_1_1_,
    products0_.created_dt as created_3_1_1_,
    products0_.description as descript4_1_1_,
    products0_.image as image5_1_1_,
    products0_.is_favorite as is_favor6_1_1_,
    products0_.price as price7_1_1_,
    products0_.title as title8_1_1_,
    products0_.updated_by as updated_9_1_1_,
    products0_.updated_dt as updated10_1_1_ 
from
    product_master products0_ 
where
    products0_.category_id=? 2022-09-25 13:39:56.507 TRACE 14160 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [2] Hibernate: 
select
    products0_.category_id as categor11_1_0_,
    products0_.id as id1_1_0_,
    products0_.id as id1_1_1_,
    products0_.category_id as categor11_1_1_,
    products0_.created_by as created_2_1_1_,
    products0_.created_dt as created_3_1_1_,
    products0_.description as descript4_1_1_,
    products0_.image as image5_1_1_,
    products0_.is_favorite as is_favor6_1_1_,
    products0_.price as price7_1_1_,
    products0_.title as title8_1_1_,
    products0_.updated_by as updated_9_1_1_,
    products0_.updated_dt as updated10_1_1_ 
from
    product_master products0_ 
where
    products0_.category_id=?

Problem statement to resolved here is that Number of Product query will be number of row in category table. We want this to be lazy load and don't want to perform multiple query for product while selecting category.

CodePudding user response:

OneToMany relationships are inherently lazy but setting Fetch( FetchMode.JOIN) will override this lazy behaviour so should be removed if you want lazy fetching of products

CodePudding user response:

Left join fetch the categories :

@NamedQuery(name = "ProductMaster.findAll", query = "SELECT p FROM ProductMaster p left join fetch p.category")

Then inside a transaction load all ProductMaster grouped by Category

private EntityManager em;
Map<Category, Set<ProductMaster>> result = em.createNamedQuery("ProductMaster.findAll", ProductMaster.class)
    .getResultStream()
    .filter(o -> o.getCategory() != null)
    .collect(Collectors.groupingBy(ProductMaster::getCategory, Collectors.toSet()));

Then outside the transaction (entites are detached) you can do :

for(Entry<Category, Set<ProductMaster>> e : result.entrySet()) {
        Category category = e.getKey();
        Set<ProductMaster> set = e.getValue();
        category.setRequests(set);
}

This will load all categories associated with at least one productMaster. Load all categories if you want those with no productMaster.

  • Related