
在复杂的企业应用中,数据模型之间往往存在多层关联。考虑以下两个hibernate实体模型:funcionario(员工)和cargo(职位)。
Funcionario 模型
Funcionario实体代表员工信息,其中包含一个与Cargo实体建立的ManyToOne关联。
@Entity
@Table(name = "funcionarios")
public class Funcionario extends Model {
// ... 其他属性
@NotFound(action = NotFoundAction.IGNORE)
@ManyToOne(fetch = FetchType.LAZY, optional = true)
private Cargo cargo;
// ... 其他属性
}Cargo 模型
Cargo实体代表职位信息,其中包含一个与Treinamento(培训)实体建立的ManyToMany关联,表示该职位所需的培训列表。
@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是延迟加载(FetchType.LAZY),Cargo与treinamentosNecessarios集合也是延迟加载。这意味着在默认情况下,当我们查询Funcionario时,其关联的Cargo对象以及Cargo对象中的treinamentosNecessarios集合都不会被立即加载,只有在首次访问时才会触发额外的数据库查询。
为了避免N+1查询问题,提高查询效率,我们通常希望在查询Funcionario时,能够同时预加载其关联的Cargo对象,以及Cargo对象内部的treinamentosNecessarios集合。
在使用Hibernate的CriteriaQuery进行预加载时,直接预加载Funcionario的Cargo属性相对简单:
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Funcionario> criteriaQuery = cb.createQuery(Funcionario.class);
Root<Funcionario> root = criteriaQuery.from(Funcionario.class);
// 预加载 Cargo 属性
root.fetch("cargo", JoinType.LEFT);
// ... 其他 fetch 或条件
criteriaQuery.select(root);
// ... 执行查询然而,如果尝试直接通过点号路径(如"cargo.treinamentosNecessarios")在Root对象上进行更深层次的集合预加载,例如:
// 这种方式通常无法直接在 Root 上工作,因为它不是直接关联的属性
// root.fetch("cargo.treinamentosNecessarios", JoinType.LEFT); 这种直接在Root上使用点号路径预加载嵌套集合的方式是无效的,因为root代表的是Funcionario实体,它不直接包含treinamentosNecessarios属性。treinamentosNecessarios是Cargo实体内部的集合。
解决此问题的关键在于理解CriteriaQuery中fetch方法的返回值。fetch方法返回一个Fetch对象,该对象代表了被预加载的关联。我们可以利用这个Fetch对象来继续预加载其内部的关联。
具体来说,当我们在Root上调用fetch("cargo", JoinType.LEFT)时,它会返回一个代表Cargo关联的Fetch对象。然后,我们可以在这个Fetch对象上再次调用fetch("treinamentosNecessarios", JoinType.LEFT),从而实现嵌套关联集合的预加载。
以下是使用链式fetch操作预加载Funcionario的Cargo及其treinamentosNecessarios集合的完整代码示例:
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Fetch; // 注意这里导入的是 javax.persistence.criteria.Fetch
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Root;
import org.hibernate.query.Query;
import org.hibernate.Session; // 假设 session 已经获取
public class FuncionarioDao {
public Funcionario findWithEagerCargoAndTreinamentos(Long id, Session session) {
try {
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Funcionario> criteriaQuery = cb.createQuery(Funcionario.class);
Root<Funcionario> root = criteriaQuery.from(Funcionario.class);
// 1. 预加载 Funcionario 的 Cargo 属性
// fetch 方法返回一个 Fetch 对象,代表了预加载的 Cargo 关联
Fetch<Funcionario, Cargo> cargoFetch = root.fetch("cargo", JoinType.LEFT);
// 2. 在 Cargo 的 Fetch 对象上继续预加载 treinamentosNecessarios 集合
// 注意:这里是 cargoFetch.fetch(...),而不是 root.fetch(...)
cargoFetch.fetch("treinamentosNecessarios", JoinType.LEFT);
// 如果还有其他需要预加载的直接关联,可以继续在 root 上调用 fetch
// root.fetch("avaliacoes", JoinType.LEFT);
// root.fetch("treinamentosRealizados", 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 new RuntimeException("查询员工及其关联信息失败", ex);
}
}
}在上述代码中,关键在于以下两行:
这种链式fetch操作的原理是,javax.persistence.criteria.Fetch接口扩展了javax.persistence.criteria.Join接口,而Join接口又扩展了javax.persistence.criteria.Path接口。Path接口提供了fetch方法,允许我们从当前的路径继续向下预加载关联。
当root.fetch("cargo", JoinType.LEFT)执行时,它实际上构建了一个从Funcionario到Cargo的连接,并标记Cargo为预加载。返回的Fetch对象本质上代表了这个连接的“终点”——即Cargo实体。因此,我们可以在这个Cargo的“终点”上继续构建到treinamentosNecessarios的连接,并将其标记为预加载。
通过本文的讲解,我们理解了如何在Hibernate的CriteriaQuery中有效地预加载子对象的嵌套关联集合。核心方法是利用fetch方法返回的Fetch对象进行链式调用,从而实现多层关联的预加载。这种技术对于优化复杂数据模型的查询性能、避免N+1查询问题至关重要。在实际开发中,应结合业务需求和性能考量,合理运用预加载策略。
以上就是使用CriteriaQuery预加载嵌套关联集合的教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号