JPA not in subqueries CriteriaBuilder

本文探讨了如何利用JPACriteriaBuilder解决强类型化实体中的子查询问题,具体场景为通过ProductOwner实体查询未拥有任何产品的记录。通过构建JPACriteriaBuilder查询,实现SQL中的子查询功能,从而高效地获取所需数据。

   

 

Writing Strongly Typed NOT IN Subqueries with JPA CriteriaBuilder

Written by Sean Ryan on May 9, 2012. Posted in Misc Ramblings

This one took a while to figure out and it required a few beers.

Let’s say you have an entity called Product and an entity called ProductOwner and there is a 1 to n relationship from ProductOwner to Product but you can only access Product from ProductOwner.

This may not be the way the database is designed – that would be terrible, but it may be the way your model has been designed. Probably an oversight but in any case, now you’re stuck with it.

What do you do if you want find out how many ProductOwners don’t actually own any products? It can happen, but because of the design of the model, it’s not obvious how to fetch those records.

In SQL, it’s easy…

select * from ProductOwner po where po.id not in (select p.id from product)

Using JPA CriteriaBuilder you would do the following.

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ProductOwner> query = cb.createQuery(ProductOwner.class);
Root<ProductOwner> poRoot = query.from(ProductOwner.class);
query.select(poRoot);

Subquery<Product> subquery = query.subquery(Product.class);
Root<Product> subRoot = subquery.from(Product.class);
subquery.select(subRoot);

Predicate p = cb.equal(subRoot.get(Product_.productOwner),poRoot);
subquery.where(p);
query.where(cb.not(cb.exists(subquery))); 

TypedQuery<ProductOwner> typedQuery = entityManager.createQuery(query);

List<ProductOwner> result = typedQuery.getResultList();
<think>我们正在处理一个关于JPA中'notin'查询的问题。用户希望了解如何在JPA中正确使用'notin'查询,并请求语法示例。根据引用内容,我们可以参考JPQL的语法。引用[3]中展示了在JPQL中使用IN运算符的示例。类似地,NOTIN运算符也是JPQL的一部分,用于检查某个值是否不在指定的集合中。在JPA中,我们可以使用JPQL(JavaPersistenceQueryLanguage)或CriteriaAPI来构建查询。这里我们主要展示JPQL的方式。NOTIN查询的基本语法如下:```sqlSELECT...FROM...WHEREfieldNOTIN(value_list)```其中,value_list可以是一个具体的值列表,也可以是一个子查询的结果。示例1:使用固定值列表假设我们有一个实体类`User`,有一个字段`name`,我们想查询不在指定名字列表中的用户。```javaStringjpql="SELECTuFROMUseruWHEREu.nameNOTIN('Alice','Bob','Charlie')";List<User>result=entityManager.createQuery(jpql,User.class).getResultList();```示例2:使用子查询假设我们有两个实体:`Department`和`Employee`,一个部门有多个员工。我们想查询那些没有员工的部门。```javaStringjpql="SELECTdFROMDepartmentdWHEREd.idNOTIN(SELECTe.department.idFROMEmployeee)";List<Department>result=entityManager.createQuery(jpql,Department.class).getResultList();```或者,我们可以使用NOTEXISTS来实现同样的效果,但这里我们专注于NOTIN。注意:如果子查询可能返回空集,NOTIN条件将始终为真(因为没有任何值在空集中),所以需要谨慎处理。在这种情况下,可以考虑使用NOTEXISTS或者确保子查询不返回空集(例如,通过添加一个永远不为真的条件来避免空集,但这通常不是最佳实践)。另外,引用[1]和[2]提到了使用ExampleAPI进行查询,但ExampleAPI主要用于通过示例对象进行查询,它不支持复杂的条件如NOTIN。因此,对于NOTIN查询,我们通常使用JPQL或CriteriaAPI。使用CriteriaAPI构建NOTIN查询的示例:```javaCriteriaBuildercb=entityManager.getCriteriaBuilder();CriteriaQuery<User>query=cb.createQuery(User.class);Root<User>user=query.from(User.class);//构建一个值列表List<String>namesToExclude=Arrays.asList("Alice","Bob","Charlie");//使用notin方法query.select(user).where(cb.not(user.get("name").in(namesToExclude)));List<User>result=entityManager.createQuery(query).getResultList();```或者使用子查询:```javaCriteriaBuildercb=entityManager.getCriteriaBuilder();CriteriaQuery<Department>query=cb.createQuery(Department.class);Root<Department>dept=query.from(Department.class);Subquery<Long>subquery=query.subquery(Long.class);Root<Employee>emp=subquery.from(Employee.class);subquery.select(emp.get("department").get("id"));query.select(dept).where(cb.not(dept.get("id").in(subquery)));List<Department>result=entityManager.createQuery(query).getResultList();```总结:-在JPA中,可以使用JPQL或CriteriaAPI来构建NOTIN查询。-NOTIN可以用于值列表或子查询。-注意子查询返回空集时的逻辑(此时NOTIN条件会返回所有记录)。最后,根据用户要求,生成相关问题。§§相关问题§§1.JPAINNOTIN查询的性能差异是什么?2.除了NOTINJPA中还有哪些方式可以实现排除某些结果的查询?3.在JPA中如何动态构建NOTIN查询的条件(例如,根据用户输入的条件)?4.使用子查询进行NOTIN操作时,如何避免子查询返回空集导致的问题?</think>### JPA 中 `NOT IN` 查询语法详解 在 JPA 中执行 `NOT IN` 查询时,可以使用 **JPQL(Java Persistence Query Language)** 或 **Criteria API**。以下是具体语法和示例: --- #### 一、基础语法 `NOT IN` 查询的基本结构: ```sql SELECT ... FROM Entity WHERE field NOT IN (:collection) ``` - `:collection` 可以是参数化集合或子查询结果 - 字段需是集合元素的同类型 --- #### 二、JPQL 实现示例 ##### 1. 直接值列表 ```java // 查询ID不在指定列表中的用户 String jpql = "SELECT u FROM User u WHERE u.id NOT IN (1, 3, 5)"; List<User> result = entityManager.createQuery(jpql, User.class).getResultList(); ``` ##### 2. 参数化集合 ```java // 动态传入排除列表 List<String> excludedNames = Arrays.asList("Alice", "Bob"); String jpql = "SELECT u FROM User u WHERE u.name NOT IN :excludedNames"; List<User> result = entityManager.createQuery(jpql, User.class) .setParameter("excludedNames", excludedNames) .getResultList(); ``` ##### 3. 子查询(推荐处理大量数据) ```java // 查询未分配部门的员工 String jpql = "SELECT e FROM Employee e " + "WHERE e.department.id NOT IN " + "(SELECT d.id FROM Department d WHERE d.status = 'ACTIVE')"; List<Employee> result = entityManager.createQuery(jpql, Employee.class).getResultList(); ``` --- #### 三、Criteria API 实现(类型安全) ```java CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<User> query = cb.createQuery(User.class); Root<User> user = query.from(User.class); // 构建排除ID集合 List<Long> excludedIds = Arrays.asList(2L, 4L, 6L); ParameterExpression<List<Long>> param = cb.parameter(List.class); query.select(user) .where(cb.not(user.get("id").in(param))); // NOT IN 条件 List<User> result = entityManager.createQuery(query) .setParameter(param, excludedIds) .getResultList(); ``` --- #### 四、注意事项 1. **空集合处理** 当 `NOT IN` 右侧为空集合时,查询会返回 **所有记录**(逻辑上等价于 `WHERE true`)。需添加额外检查: ```sql WHERE (:ids IS EMPTY OR u.id NOT IN :ids) ``` 2. **性能优化** - 对大数据集优先使用子查询而非内存传参 - 确保关联字段有索引(如 `department.id`) 3. **类型匹配** JPQL 要求 `NOT IN` 两侧类型严格一致,例如 `Long` 类型字段不能与 `String` 列表比较[^3]。 4. **关联实体查询** 多表关联时可结合 `JOIN`(引用[4]的实体关系): ```sql SELECT u FROM User u WHERE u.id NOT IN ( SELECT a.user.id FROM Address a WHERE a.city = 'Beijing' ) ``` --- #### 五、完整示例 假设有实体 `Department` 和 `Employee`: ```java // 查询不在研发部的员工 String jpql = "SELECT e FROM Employee e " + "WHERE e.department.id NOT IN " + "(SELECT d.id FROM Department d WHERE d.name = 'R&D')"; List<Employee> nonRdEmployees = entityManager .createQuery(jpql, Employee.class) .getResultList(); ``` > **提示**:对于动态条件组合,推荐使用 Criteria API 避免 SQL 注入风险[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值