我正在使用Spring Boot,Spring Data和Hibernate开发应用程序。我有一个实体“ Client”,它与另一个实体“ Reservation”具有“ onetomany”的关系,如下所示:
@Entity
public class Client implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany( mappedBy = "client", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.JOIN)
private Set<Reservation> reservations = new HashSet<>();
}
我已经实现了JpaRepository接口,因此可以操纵客户端数据:
public interface ClientRepository extends JpaRepository<Client, Long> {}
在服务类中,我实现了一种在数据库中查找所有客户端的方法:
@Service
public class ClientService {
@Autowired
private ClientRepository clientRepository;
@Transactional(readOnly = true)
public List<Client> findAll() {
return clientRepository.findAll();
}}
findAll()的执行效果很好,并返回了所有客户端及其保留。但是,在我的sql日志中,尽管我已经在我的客户实体中设置了fetch=FetchType.EAGER
,但我已经执行了N + 1个查询(客户数为N)。我以为hibernate将创建一个查询,其中将所有必要的数据连接起来以获取客户及其重新定位信息。
因此,我被迫通过查询显式加入客户端和预订:
@Query("select client from Client client left join fetch client.reservations")
public List<Client> findAllEager();
我还发现了另一种选择,该选择仅允许执行两个查询来检索所有客户端及其保留:
@OneToMany(mappedBy = "client", fetch = FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
我注意到从JPA eager fetch does not join的讨论来看,@OneToMany(fetch=FetchType.EAGER) @Fetch(value = FetchMode.JOIN)
只会执行一个查询来检索结果,这与我的情况无关。
OneToMany(fetch=FetchType.EAGER)
是否执行N + 1个查询以获取数据?
答案 0 :(得分:1)
@OneToMany(fetch=FetchType.EAGER)
仅定义何时提取相关实体,而不定义如何提取实体。 (即是通过单个联接的SQL还是多个分离的SQL提取),因此它可以执行N + 1个查询。
@Fetch(value = FetchMode.JOIN)
定义如何获取相关实体。但是,仅当使用ID直接获取实体时(即通过entityManager.find(Client.class, 100)
获取实体,但对JPQL查询或Criteria API无效。)被Hibernate用户指南称为below):
之所以不使用JPQL查询来获取多个 部门实体是因为FetchMode.JOIN策略是 被查询获取指令覆盖。
要通过JPQL查询获取多个关系,请使用JOIN FETCH 指令必须改为使用。
因此,FetchMode.JOIN在获取实体时非常有用 直接通过其标识符或自然ID。
Spring数据在内部使用Criteria API进行findAll()
查询,因此@Fetch(value = FetchMode.JOIN)
不会有任何效果。您必须显式使用JOIN FETCH
查询来避免N + 1个查询。