使用Criteria API查询关联AEntity名称包含指定全部值的BEntity
实现方案
核心思路
你用in表达式只能匹配到关联了任意一个目标AEntity的BEntity,无法实现全匹配。正确的做法是:
- 关联BEntity和AEntity的多对多关系
- 过滤出AEntity名称在目标列表中的记录
- 按BEntity主键分组
- 统计每个BEntity匹配到的AEntity数量,只有数量等于目标列表长度的才是同时包含所有目标AEntity的记录
完整Criteria API实现代码
import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.criteria.*; import org.springframework.stereotype.Repository; import java.util.Collections; import java.util.List; @Repository public class BRepositoryImpl { @PersistenceContext private EntityManager entityManager; public List<BEntity> mySearchMethod(List<String> targetANameList) { // 边界处理:空列表可根据业务需求调整返回逻辑 if (targetANameList == null || targetANameList.isEmpty()) { return Collections.emptyList(); } CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<BEntity> query = cb.createQuery(BEntity.class); Root<BEntity> bRoot = query.from(BEntity.class); // 内连接关联AEntity集合 Join<BEntity, AEntity> aJoin = bRoot.join("aEntities", JoinType.INNER); // 过滤名称在目标列表的AEntity Predicate nameInPredicate = aJoin.get("name").in(targetANameList); query.where(nameInPredicate) // 按BEntity主键分组 .groupBy(bRoot.get("id")) // 匹配到的A数量等于目标列表长度,说明包含所有目标A .having(cb.equal(cb.countDistinct(aJoin.get("id")), targetANameList.size())); return entityManager.createQuery(query).getResultList(); } }
实现原理
拿你的示例数据举例,传入目标列表["A1", "A3"]:
- 内连接后会把每个B和关联的A拆分为独立行:
B1-A1、B1-A2、B1-A3、B2-A1、B2-A3、B3-A3 - 经过where过滤后剩下:
B1-A1、B1-A3、B2-A1、B2-A3、B3-A3 - 按B的id分组后,各组匹配到的A数量分别为:B1=2、B2=2、B3=1
- having筛选数量等于2的分组,最终返回B1和B2,完全符合预期
补充说明
- 如果AEntity的name不唯一,建议先根据name列表查询到对应的AEntity主键集合,再用主键做in查询和计数,避免计数错误
- 如果使用Spring Data JPA,也可以直接用JPQL实现,逻辑完全一致:
import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface BRepository extends JpaRepository<BEntity, Long> { @Query("SELECT b FROM BEntity b JOIN b.aEntities a WHERE a.name IN :names GROUP BY b.id HAVING COUNT(DISTINCT a.id) = :#{#names.size()}") List<BEntity> mySearchMethod(@Param("names") List<String> targetANameList); }
内容的提问来源于stack exchange,提问作者Razor
相关产品推荐
相关产品推荐

