javax.persistence.criteria.CriteriaBuilder#or工作原理及无返回结果问题咨询
问题根因
你遇到的这个现象由两个错误共同导致:
- 隐式内连接过滤了有效数据
你通过root.get("a").get("b").get("y")这种连续调用获取属性路径的写法,JPA CriteriaAPI 会默认生成INNER JOIN关联对应实体。只要任意X实例的c属性为空、或c关联的d属性为空,这条实例在执行关联时就会被提前排除,哪怕它满足p1的匹配条件也不会进入后续的谓词判断环节。
单独使用p1时只会生成a、b的内连接,只要这两个关联不为空且y值匹配就能返回结果;一旦你用到了path2的路径,不管你用or还是and组合条件,都会额外生成c、d的内连接,把c/d为空的有效行直接过滤,最终导致or组合后无结果返回。 - 查询相关对象作为单例Bean的成员变量,存在线程安全问题
Spring 中@Repository注解的Bean默认是单例模式,你把CriteriaBuilder、CriteriaQuery、Root这些对象定义为类成员变量,只会在Bean初始化时创建一次,每次调用filter方法都会复用同一个query对象,多次调用会叠加where条件,会出现各种不可预期的查询结果。
修复方案
1. 手动指定左连接替代隐式内连接
把连续get属性的写法改为手动构造左连接,避免关联为空时提前过滤数据:
// 每次查询重新构造join,不要复用 Join<X, A> aJoin = root.join("a", JoinType.LEFT); Join<A, B> bJoin = aJoin.join("b", JoinType.LEFT); Expression<?> path1 = bJoin.get("y"); Join<X, C> cJoin = root.join("c", JoinType.LEFT); Join<C, D> dJoin = cJoin.join("d", JoinType.LEFT); Expression<?> path2 = dJoin.get("y");
2. 每次查询重新创建查询相关对象
不要把CriteriaQuery、Root作为类成员变量,每次调用filter方法时重新创建,保证每次查询独立且线程安全,修改后的代码示例如下:
@Repository public class CustomXRepositoryImpl implements CustomXRepository { private final EntityManager em; @Autowired public CustomXRepositoryImpl(EntityManager em) { this.em = em; } @Override public List<X> filter(FilterParams params) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<X> query = builder.createQuery(X.class); Root<X> root = query.from(X.class); List<Predicate> restrictions = new ArrayList<>(); // 条件逻辑 if (params.getY() != null) { // 此处插入上面的左连接构造逻辑 Predicate p1 = builder.equal(path1, params.getY()); Predicate p2 = builder.equal(path2, params.getY()); restrictions.add(builder.or(p1, p2)); } // 其余条件逻辑不变 query.where(restrictions.toArray(new Predicate[0])); return em.createQuery(query).getResultList(); } }
内容的提问来源于stack exchange,提问作者dr1be
相关产品推荐
相关产品推荐

