条件 API 忽略 LAZY 提取
Criteria API ignore LAZY fetch
我有两个实体 Customer
和 Product
具有惰性策略的一对多关系。
问题是获取不懒惰,产品集合总是获取所有相关产品。
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());
}
正如你所看到的,没有人提到 fetch eager,和实体 类 一样,都是懒惰的:
客户
@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;
}
产品
@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;
}
为什么 lazy work as eager 以及如何在这种情况下启用真正的懒惰模式?
UPDATE_1
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()));
}
我打电话给 .getProducts()
但预计 LazyInitializationException
因为会话已经关闭。
我猜你正在使用 Spring?尝试禁用 spring.jpa.properties.hibernate.enable_lazy_load_no_trans
无论如何这是一种反模式。
原因是 EntityManager
保持开放 session。并按需加载子实体。就我而言 .getProducts()
.
关闭session直接使用entityManager.close()
.
我有两个实体 Customer
和 Product
具有惰性策略的一对多关系。
问题是获取不懒惰,产品集合总是获取所有相关产品。
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());
}
正如你所看到的,没有人提到 fetch eager,和实体 类 一样,都是懒惰的:
客户
@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;
}
产品
@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;
}
为什么 lazy work as eager 以及如何在这种情况下启用真正的懒惰模式?
UPDATE_1
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()));
}
我打电话给 .getProducts()
但预计 LazyInitializationException
因为会话已经关闭。
我猜你正在使用 Spring?尝试禁用 spring.jpa.properties.hibernate.enable_lazy_load_no_trans
无论如何这是一种反模式。
原因是 EntityManager
保持开放 session。并按需加载子实体。就我而言 .getProducts()
.
关闭session直接使用entityManager.close()
.