Search code examples
javahibernatecriteriahibernate-criteriacriteria-api

Criteria API ignore LAZY fetch


I have two entities Customer and Product with relation one to many with lazy strategy.

Problem is fetch not lazy, products collection always fetch all related products.

public Optional<Customer> findById(final long customerId) {
    final EntityManager entityManager = sessionFactory.createEntityManager();
    final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    final CriteriaQuery<Customer> criteriaQuery = criteriaBuilder.createQuery(Customer.class);
    final Root<Customer> customerMetamodel = criteriaQuery.from(Customer.class);
    criteriaQuery.where(criteriaBuilder.equal(customerMetamodel.get("id"), customerId));
    final TypedQuery<Customer> query = entityManager.createQuery(criteriaQuery);
    return Optional.ofNullable(query.getSingleResult());
} 

As you can see no one mention about fetch eager, and in entity classes the same, all lazy:

Customer

@Data
@Builder
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "customer")
public class Customer {

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

    private String name;

    private int age;

    @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
    private List<Product> products;

}

Product

@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString(exclude = "customer")
@Table(name = "product")
public class Product {

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

    private BigDecimal price;

    @ManyToOne(fetch = FetchType.LAZY)
    private Customer customer;

}

Why lazy work as eager and how to enable real lazy mode in this case?

UPDATE_1

Integration test with H2

@Test
public void testFindById() {
    Customer customer = customerRepository.create(Customer.builder().age(20).name("Denis").build());
    productRepository.create(Product.builder().customer(customer).price(new BigDecimal(100)).build());
    productRepository.create(Product.builder().customer(customer).price(new BigDecimal(200)).build());
    productRepository.create(Product.builder().customer(customer).price(new BigDecimal(300)).build());


    final Customer result = customerRepository.findById(customer.getId()).orElseThrow();

    assertEquals("Denis", result.getName());
    assertThrows(LazyInitializationException.class, () -> System.out.println(result.getProducts()));
}

I call .getProducts() but expect LazyInitializationException because session already closed.


Solution

  • I guess you are using Spring? Try disabling spring.jpa.properties.hibernate.enable_lazy_load_no_trans which is an anti-pattern anyway.