如何使用javax.jpa.CriteriaQuery实现双根实体查询?(Hibernate迁移)
没问题,我来帮你把Hibernate的多根Criteria转换为JPA CriteriaQuery。其实JPA Criteria API完全支持多根实体查询,核心是通过添加多个Root对象或者使用关联查询来对应原来的Hibernate分层createCriteria调用,我分两种常见场景给你举例说明:
场景1:无关联的双根实体(笛卡尔积查询)
如果你的Hibernate Criteria是直接创建两个独立的实体根(没有关联关系,本质是笛卡尔积查询),原来的写法大概是这样:
// 原Hibernate Criteria 双根写法 Criteria criteria = session.createCriteria(Order.class, "order"); criteria.createCriteria(User.class, "user"); // 添加第二个独立根实体 // 添加过滤条件 criteria.add(Restrictions.eq("order.status", OrderStatus.PAID)); criteria.add(Restrictions.eq("user.role", UserRole.ADMIN)); List<?> results = criteria.list();
对应的JPA CriteriaQuery实现,需要通过query.from()添加多个Root,并用Tuple来接收多实体结果:
// JPA CriteriaQuery 双根实现 EntityManager em = ...; // 获取你的EntityManager实例 CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Tuple> query = cb.createTupleQuery(); // 添加两个独立的根实体 Root<Order> orderRoot = query.from(Order.class); Root<User> userRoot = query.from(User.class); // 指定要返回的实体/字段,这里选择两个完整根实体 query.multiselect(orderRoot, userRoot); // 添加过滤条件 Predicate orderStatusPred = cb.equal(orderRoot.get("status"), OrderStatus.PAID); Predicate userRolePred = cb.equal(userRoot.get("role"), UserRole.ADMIN); query.where(cb.and(orderStatusPred, userRolePred)); // 执行查询并处理结果 List<Tuple> results = em.createQuery(query).getResultList(); for (Tuple tuple : results) { Order order = tuple.get(orderRoot); User user = tuple.get(userRoot); // 处理业务逻辑 }
如果需要返回自定义DTO而不是Tuple,可以用构造函数表达式:
CriteriaQuery<OrderUserDTO> query = cb.createQuery(OrderUserDTO.class); Root<Order> orderRoot = query.from(Order.class); Root<User> userRoot = query.from(User.class); // 调用DTO的构造函数封装结果 query.select(cb.construct(OrderUserDTO.class, orderRoot.get("id"), userRoot.get("name")));
场景2:关联的双根实体(分层关联查询)
很多时候Hibernate的createCriteria分层调用是用来关联子实体(比如从订单关联到用户),而不是真正的独立双根,原写法大概是:
// 原Hibernate Criteria 关联分层写法 Criteria criteria = session.createCriteria(Order.class, "order"); // 通过订单的user关联字段,创建子Criteria criteria.createCriteria("order.user", "user") .add(Restrictions.eq("user.role", UserRole.ADMIN)); criteria.add(Restrictions.eq("order.status", OrderStatus.PAID)); List<Order> orders = criteria.list();
这种场景下,JPA CriteriaQuery不需要添加第二个Root,而是用join来实现关联查询:
// JPA CriteriaQuery 关联查询实现 EntityManager em = ...; CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Order> query = cb.createQuery(Order.class); Root<Order> orderRoot = query.from(Order.class); // 关联到订单的user实体,对应原分层createCriteria Join<Order, User> userJoin = orderRoot.join("user", JoinType.INNER); // 添加过滤条件 Predicate orderStatusPred = cb.equal(orderRoot.get("status"), OrderStatus.PAID); Predicate userRolePred = cb.equal(userJoin.get("role"), UserRole.ADMIN); query.where(cb.and(orderStatusPred, userRolePred)); // 执行查询 List<Order> orders = em.createQuery(query).getResultList();
额外注意点
- 多根查询本质是笛卡尔积,如果没有关联过滤条件,会返回两个实体的所有组合,一定要添加合适的条件避免数据爆炸。
- 如果原Hibernate代码是用
createCriteria创建子查询,JPA里可以用Subquery对象来实现:
// JPA子查询示例 Subquery<Long> subquery = query.subquery(Long.class); Root<User> subUserRoot = subquery.from(User.class); subquery.select(subUserRoot.get("id")).where(cb.equal(subUserRoot.get("role"), UserRole.ADMIN)); query.where(orderRoot.get("userId").in(subquery));
内容的提问来源于stack exchange,提问作者macmeyers50
相关产品推荐
相关产品推荐

