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.
I guess you are using Spring? Try disabling spring.jpa.properties.hibernate.enable_lazy_load_no_trans
which is an anti-pattern anyway.