如何使用CriteriaQuery预获取子对象中的treinamentosNecessarios集合?
问题:JPA CriteriaQuery预获取嵌套关联集合
我有两个实体类:
实体类 Funcionario
@Entity @Table(name = "funcionarios") public class Funcionario extends Model { .... @NotFound(action = NotFoundAction.IGNORE) @ManyToOne(fetch = FetchType.LAZY, optional = true) private Cargo cargo; ... }
实体类 Cargo
@Entity @Table(name = "cargos") public class Cargo extends Model { @Column(nullable = false, unique = true, columnDefinition = "TEXT") private String cargo = ""; @ManyToMany(fetch = FetchType.LAZY) private Set<Treinamento> treinamentosNecessarios; }
我尝试加载Funcionario实体时,预获取其关联的Cargo以及Cargo中的treinamentosNecessarios集合。目前代码能获取到Cargo,但无法加载treinamentosNecessarios。请问如何通过CriteriaQuery实现从Cargo中预获取这个集合?
现有代码:
@Override public Funcionario find(Long id, Session session, boolean closeSession) { try { CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Funcionario> criteriaQuery = cb.createQuery(Funcionario.class); Root<Funcionario> root = criteriaQuery.from(Funcionario.class); root.fetch("avaliacoes", JoinType.LEFT); root.fetch("treinamentosRealizados", JoinType.LEFT); root.fetch("cargo", JoinType.LEFT); //root.fetch("cargo.treinamentosNecessarios", JoinType.LEFT); criteriaQuery.select(root); criteriaQuery.where(cb.equal(root.get("id"), id)); Query<Funcionario> query = session.createQuery(criteriaQuery); Funcionario singleResult = query.getSingleResult(); return singleResult; } catch (Exception ex) { throw ex; } finally { if (session != null && closeSession) { session.close(); } } }
解决方案
要预获取嵌套的treinamentosNecessarios集合,不能直接使用root.fetch("cargo.treinamentosNecessarios")这种点路径写法,需要先获取cargo对应的Fetch对象,再通过该对象继续预获取嵌套集合。修改后的代码如下:
@Override public Funcionario find(Long id, Session session, boolean closeSession) { try { CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Funcionario> criteriaQuery = cb.createQuery(Funcionario.class); Root<Funcionario> root = criteriaQuery.from(Funcionario.class); root.fetch("avaliacoes", JoinType.LEFT); root.fetch("treinamentosRealizados", JoinType.LEFT); // 先获取cargo的Fetch对象 Fetch<Funcionario, Cargo> cargoFetch = root.fetch("cargo", JoinType.LEFT); // 通过cargoFetch继续预获取treinamentosNecessarios集合 cargoFetch.fetch("treinamentosNecessarios", JoinType.LEFT); criteriaQuery.select(root); criteriaQuery.where(cb.equal(root.get("id"), id)); Query<Funcionario> query = session.createQuery(criteriaQuery); Funcionario singleResult = query.getSingleResult(); return singleResult; } catch (Exception ex) { throw ex; } finally { if (session != null && closeSession) { session.close(); } } }
关键说明
- 嵌套关联的预获取需要逐层处理:先获取一级关联的Fetch对象,再基于该对象调用
fetch方法加载下一级关联,不能直接使用点分隔的路径字符串。 - 这种写法会生成包含多层左连接的SQL语句,一次性加载
Funcionario、Cargo和treinamentosNecessarios的所有数据,避免懒加载触发的N+1查询问题。
内容的提问来源于stack exchange,提问作者KenobiBastila
相关产品推荐
相关产品推荐

