spring JPA 中IN 的用法

博客讨论了在JPA和MyBatis中构建复杂查询的差异,尤其是处理空值和子查询的情况。作者指出,JPA的SQL构建不如MyBatis灵活,对于大量数据的查询可能导致性能问题。文中给出了一种优化SQL查询的建议,即避免使用`UNION`以提高性能,并展示了如何在JPA中实现类似逻辑的SQL查询。此外,还提供了具体的JPA查询代码实现作为示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一些语法并不向mybatis那样灵活,写起来也是很费劲

1. 查询条件

比如:

        要实现某个查询条件,要满足

1. 改字段为空的场景,

2. 该字段不为空,但是要根据该条件再过滤一次的场景

2. 常规的sql两种实现方法

① select * from  a   A where A.sys_id is null union select * from a A  where A.sys_id is not null and A.sys_id in (select id from B where ..... );

② select * from  a   A where  A.sys_id is null or   A.sys_id in (select id from B where ..... );

以上两种sql对比,如果条件较少,①还可以,如果条件较多,并不怎么好写,同时性能

union要进行一次去重的操作。

我这个表大概50W条数据,而且不重复。所以系统每插入一条数据,就要走全表查一次索引,50W条数据就要查50W次,当然卡死。

提出建议:使用union all。这样就不会走去重的逻辑了。

因此想到了使用sql②,但是用jpa的sql语法并不像mybatis的写法那么灵活,该怎么写呢?

3 以下是jpa的sql

select ftagentpo0_.agentid as agentid1_1_, ftagentpo0_.clusterstate as clusters2_1_, ftagentpo0_.description as descript3_1_, ftagentpo0_.hport as hport4_1_, ftagentpo0_.agentip as agentip5_1_, ftagentpo0_.ismonitor as ismonito6_1_, ftagentpo0_.ismanually as ismanual7_1_, ftagentpo0_.agentname as agentnam8_1_, ftagentpo0_.password as password9_1_, ftagentpo0_.port as port10_1_, ftagentpo0_.priority as priorit11_1_, ftagentpo0_.serverid as serveri12_1_, ftagentpo0_.status as status13_1_, ftagentpo0_.sysid as sysid14_1_, ftagentpo0_.transferstatus as transfe15_1_, ftagentpo0_.updatetime as updatet16_1_, ftagentpo0_.agentver as agentve17_1_ from ft_agent ftagentpo0_ where ftagentpo0_.transferstatus=? and (ftagentpo0_.sysid is null or ftagentpo0_.sysid in (select ftsyspo1_.id from ft_sys ftsyspo1_ where ftsyspo1_.userid=?)) order by ftagentpo0_.sysid desc, ftagentpo0_.status asc, ftagentpo0_.agentid desc limit ?

4. 代码实现

protected Specification<FtAgentPO> createSpec(FtAgentPO.Criteria criteria,String type) {
        Specification<FtAgentPO> spec = (root, query, builder) -> {
            if (criteria == null) {
                return builder.and(new Predicate[]{});
            }
            List<Predicate> predicates = new ArrayList<Predicate>();
            Optional.ofNullable(criteria.getSysId()).ifPresent((value) -> predicates.add(builder.equal(root.get("sysId"), value)));
            Optional.ofNullable(criteria.getServerId()).ifPresent((value) -> predicates.add(builder.equal(root.get("serverId"), value)));
            Optional.ofNullable(criteria.getIp()).ifPresent((value) -> predicates.add(JPAPredicateHelper.like(builder, root.get("ip"), value)));
            Optional.ofNullable(criteria.getStatus()).ifPresent((value) -> predicates.add(builder.equal(root.get("status"), value)));
            Optional.ofNullable(criteria.getZone()).ifPresent((value) -> predicates.add(builder.equal(root.get("zone"), value)));
            Optional.ofNullable(criteria.getEnv()).ifPresent((value) -> predicates.add(builder.equal(root.get("env"), value)));
            Optional.ofNullable(criteria.getClusterState()).ifPresent((value) -> predicates.add(builder.equal(root.get("clusterState"), value)));
            if(type.equals("eq")){
                Optional.ofNullable(criteria.getName()).ifPresent((value) -> predicates.add(builder.equal(root.get("name"), value)));
            }else {
                Optional.ofNullable(criteria.getName()).ifPresent((value) -> predicates.add(JPAPredicateHelper.like(builder, root.get("name"), value)));
            }
            Optional.ofNullable(criteria.getTransferStatus()).ifPresent((value) -> predicates.add(builder.equal(root.get("transferStatus"), value)));
            if (criteria.getSysIdIsNull() != null) {
                if (criteria.getSysIdIsNull()) {
                    predicates.add(builder.isNull(root.get("sysId")));
                } else {
                    predicates.add(builder.isNotNull(root.get("sysId")));
                    handleUserSys(root, query, builder, predicates);
                }
            } else {
                handleUserSys(root, query, builder, predicates);
            }
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        return spec;
    }
   private void handleUserSys(Root<FtAgentPO> root, CriteriaQuery<?> query, CriteriaBuilder builder, List<Predicate> predicates) {
        if (!BFTModelHelper.checkAdmin()){
            String userId = BFTModelHelper.getUserId();

            Subquery<String> subsubquery = query.subquery(String.class);
            Root<FtSysPO> subsubroot = subsubquery.from(FtSysPO.class);
            subsubquery.select(subsubroot.get("id"));
            subsubquery.where(builder.equal(subsubroot.get("userId"), userId));

            Predicate managePredicate = builder.or(
                    builder.isNull(root.get("sysId")),
                    root.get("sysId").in(subsubquery)
            );
            predicates.add(managePredicate);
        }
    }

主要是以上的sql写法。

<think>好的,我现在需要回答用户关于Spring Data JPA核心关键字的问题。首先,我得回忆一下Spring Data JPA的基本知识,特别是它的查询方法派生机制。用户可能想知道如何通过方法名自动生成查询,而不需要写具体的实现或者JPQL。 首先,核心关键字有哪些呢?常见的比如findBy、readBy、queryBy、countBy、existsBy,这些是方法前缀,用于不同类型的操作。然后是属性表达式,比如在方法名中添加字段名,如findByUsername。接下来是条件关键字,比如And、Or、Between、LessThan、GreaterThan、Like、In、OrderBy等等。每个关键字对应不同的查询条件组合方式。 需要分清楚不同的关键字的作用,例如And连接两个条件,Between用于范围查询,Like用于模糊匹配,还有忽略大小写的处理,比如在方法名中加入IgnoreCase。另外,分页和排序的关键字,比如使用Pageable参数和OrderBy来指定排序字段和方向。 还要注意方法名的构造规则,例如属性名的驼峰命名转换为数据库字段的下划线形式,或者通过@Column注解指定字段名。另外,关键字的使用顺序可能会有影响,比如OrderBy应该放在最后。 用户可能想知道这些关键字的实际使用例子,比如如何组合多个条件,如何处理大小写敏感的问题,如何进行分页查询等。需要给出一些示例代码,帮助用户理解如何应用这些关键字。 此外,可能还需要提到一些高级的关键字,比如StartingWith、EndingWith、Containing,这些是Like的不同形式,简化了模糊查询的写法。还有True和False用于布尔字段的查询,Null和NotNull用于检查字段是否为null。 还要注意,有些关键字可能有不同的变体,比如IsNull和Null是等价的,同样IsTrue和True也是等价的。需要指出这些等效的关键字,避免用户混淆。 最后,可能需要提醒用户一些常见的陷阱,比如属性路径的正确性,避免在方法名中使用不存在的字段,或者处理关联实体时的嵌套属性。例如,findByUserDepartmentName中的User是当前实体的关联属性,Department是User的关联属性,Name是Department的字段,需要确保这些关联关系正确存在。 总结来说,回答需要涵盖核心关键字的分类、具体用法、示例代码以及注意事项,帮助用户全面理解并正确使用这些关键字来简化Repository层的开发。</think>以下是 Spring Data JPA 的核心查询关键字整理,通过方法名自动生成查询语句的规则详解: --- ### **一、基础查询结构** | 关键字前缀 | 作用描述 | 示例方法 | |------------------|--------------------------|----------------------------------------| | `findBy` | 查询返回结果集 | `findByUsername(String name)` | | `readBy` | 同`findBy`(可读性优化) | `readByEmailContaining(String keyword)`| | `queryBy` | 同`findBy`(语义化强调) | `queryByStatus(Status status)` | | `getBy` | 同`findBy`(结果唯一时用) | `getById(Long id)` | | `countBy` | 返回统计数量 | `countByActiveTrue()` | | `existsBy` | 判断是否存在记录 | `existsByEmail(String email)` | | `deleteBy` | 删除符合条件的记录 | `deleteByExpiredAtBefore(Date date)` | --- ### **二、条件组合关键字** #### **逻辑运算符** ```java // AND 组合 List<User> findByFirstNameAndLastName(String firstName, String lastName); // OR 组合 List<User> findByFirstNameOrLastName(String name1, String name2); // NOT 取反 List<User> findByFirstNameNot(String excludeName); ``` #### **比较运算符** ```java // 相等判断(可省略 Is) List<User> findByAgeIs(int age); // 等效于 findByAge(int age) // 不等判断 List<User> findByAgeNot(int age); // 范围查询 List<User> findByAgeBetween(int start, int end); // 大于/小于 List<User> findByAgeGreaterThan(int minAge); List<User> findByAgeLessThanEqual(int maxAge); ``` #### **空值判断** ```java List<User> findByAddressIsNull(); List<User> findByAddressIsNotNull(); ``` #### **集合操作** ```java // IN 查询 List<User> findByRoleIn(List<String> roles); // 忽略大小写 IN 查询 List<User> findByRoleInIgnoreCase(Collection<String> roles); ``` --- ### **三、模糊匹配与字符串处理** ```java // 左模糊(%value) List<User> findByUsernameStartingWith(String prefix); // 右模糊(value%) List<User> findByUsernameEndingWith(String suffix); // 全模糊(%value%) List<User> findByUsernameContaining(String infix); // 等价写法 List<User> findByUsernameLike(String pattern); // 需手动加 % // 大小写敏感控制 List<User> findByUsernameIgnoreCase(String name); ``` --- ### **四、时间与日期处理** ```java // 日期区间 List<Order> findByOrderDateBetween(Date start, Date end); // 早于指定时间 List<Order> findByCreatedAtBefore(Date deadline); // 晚于指定时间 List<Order> findByCreatedAtAfter(Date startTime); ``` --- ### **五、排序与分页** ```java // 静态排序 List<User> findByAgeGreaterThanOrderByLastNameAsc(int minAge); // 动态排序(通过参数) List<User> findByLastName(String lastName, Sort sort); // 分页查询(返回 Page 对象) Page<User> findByActiveTrue(Pageable pageable); ``` --- ### **六、关联对象查询** ```java // 单层关联(User 实体中有 Address 属性) List<User> findByAddressCity(String city); // 多层嵌套关联(Order -> User -> Department) List<Order> findByUserDepartmentName(String deptName); // 集合属性查询(User 有 List<Role> 属性) List<User> findByRolesName(String roleName); ``` --- ### **七、特殊类型处理** #### **Boolean 类型** ```java List<User> findByActiveTrue(); // WHERE active = true List<User> findByActiveFalse(); // WHERE active = false List<User> findByActive(boolean flag);// 通用写法 ``` #### **枚举类型** ```java enum Status { ACTIVE, INACTIVE } List<User> findByStatus(Status status); // 自动匹配枚举值 ``` --- ### **八、复合条件优先级** ```java // 组合条件示例 List<User> findByFirstNameOrLastNameAndAgeGreaterThan( String firstName, String lastName, int minAge ); // 生成 SQL: WHERE (first_name = ?1 OR last_name = ?2) AND age > ?3 ``` --- ### **九、自定义处理规则** #### **属性别名(@Param + JPQL)** ```java @Query("SELECT u FROM User u WHERE u.username = :name") List<User> findByCustomName(@Param("name") String username); ``` #### **忽略属性大小写全局配置** ```properties spring.data.jpa.repositories.query.enable-default-ignore-case=true ``` --- ### **常见陷阱与建议** 1. **属性路径验证**: 方法名中的属性必须存在于实体类中(如`findByInvalidField`会启动报错) 2. **参数顺序匹配**: 方法参数的顺序必须与方法名中的条件顺序严格一致 3. **性能优化提示**: 复杂查询建议使用 `@Query` 自定义 JPQL,避免过长的派生方法名 4. **分页最佳实践**: 分页查询应始终返回 `Page<T>` 类型而非 `List<T>`,以便获取总页数等信息 --- **完整文档参考**: [Spring Data JPA 官方文档 - 查询方法关键字](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值