为何Criteria API的.not()方法无法正常工作?
问题根源
你写的查询里用了fromProject.joinList("events"),这是内连接——内连接的特性就是只保留两边都有匹配数据的记录,所以这一步已经把所有没有Event的Project过滤掉了。之后你再对这些有Event的Project ID取not(),相当于从已经被筛过的集合里排除它们,结果当然是空的。
正确写法
给你两种靠谱的实现方式,都能准确查出没有关联Event的Project:
方式一:左连接+判断关联为空
用左连接保留所有Project,然后挑出那些没有关联Event的:
public List<Project> findAllWithoutEvents(ProjectFilter filter) { Session session = sessionFactory.openSession(); CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Project> cr = cb.createQuery(Project.class); Root<Project> fromProject = cr.from(Project.class); // 左连接events,哪怕没有关联也会保留Project记录 Join<Project, Event> eventsLeftJoin = fromProject.join("events", JoinType.LEFT); cr.select(fromProject) .where(cb.isNull(eventsLeftJoin.get("id"))) .distinct(true); // 去重,避免左连接可能带来的重复数据 Query<Project> query = session.createQuery(cr); return query.getResultList(); }
方式二:NOT EXISTS子查询
通过子查询检查是否存在关联的Event,只保留不存在的Project:
public List<Project> findAllWithoutEvents(ProjectFilter filter) { Session session = sessionFactory.openSession(); CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Project> cr = cb.createQuery(Project.class); Root<Project> fromProject = cr.from(Project.class); // 子查询:找和当前Project关联的Event Subquery<Long> subquery = cr.subquery(Long.class); Root<Event> eventRoot = subquery.from(Event.class); subquery.select(eventRoot.get("id")) .where(cb.equal(eventRoot.get("project").get("id"), fromProject.get("id"))); // 筛选没有关联Event的Project cr.select(fromProject) .where(cb.not(cb.exists(subquery))); Query<Project> query = session.createQuery(cr); return query.getResultList(); }
额外提示
- 方式一的
distinct(true)最好加上,虽然单纯查无Event的Project时不会有重复,但如果之后加其他关联条件,左连接容易产生重复记录,提前加上更稳妥。 - 方式二的NOT EXISTS性能通常比NOT IN好,尤其是数据量大的时候,而且不会遇到NOT IN因NULL值导致的异常结果,逻辑更可靠。
内容的提问来源于stack exchange,提问作者Smasher
相关产品推荐
相关产品推荐

