【spring data jpa】jpa中criteria拼接in查询

本文介绍了一种使用Java和Spring Data JPA实现的商品类型查询条件构造方法。通过示例代码展示了如何根据不同的属性(如名称、UID等)来构建复杂的查询条件,并利用拼音进行模糊匹配。

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

 示例代码:

 

public static Specification<GoodsType> where(final GoodsType goodsType){
        
        
        return new Specification<GoodsType>() {
            
            @Override
            public Predicate toPredicate(Root<GoodsType> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                
                List<Predicate> predicates = new ArrayList<Predicate>();  
                //名字+拼音
                String name = goodsType.getName();
                if(StringUtils.isNotBlank(name)){
                    Predicate like1 = cb.like(root.<String>get("name"), "%" + name +"%");
                    Predicate like2 = cb.like(root.<String>get("pyAll"), "%" + PingYinUtil.getPingYin(name) +"%");
                    Predicate like3 = cb.like(root.<String>get("pyHead"), "%" + PingYinUtil.getPinYinHeadChar(name) +"%");
                    predicates.add(cb.or(like1,like2,like3));
                }
                
                //uid
                String uid = goodsType.getUid();
                if(StringUtils.isNotBlank(uid)){
                    predicates.add(cb.equal(root.<String>get("uid"),uid));
                }
                
                //parentUid
                String parentUid = goodsType.getParentUid();
                if(StringUtils.isNotBlank(parentUid)){
                    predicates.add(cb.equal(root.<String>get("parentUid"),parentUid));
                    
                }
                //启用 状态
                Integer enabledFlag = goodsType.getEnabledFlag();
                if (enabledFlag != null){
                    predicates.add(cb.equal(root.get("enabledFlag"),enabledFlag));
                }

                //层级
                Integer floor = goodsType.getFloor();
                if (floor != null){
                    predicates.add(cb.equal(root.get("floor"),floor));
                }
                
                //对外编码
                String outerCode = goodsType.getOuterCode();
                if(StringUtils.isNotBlank(outerCode)){
                    
                    predicates.add(cb.equal(root.<String>get("outerCode"),outerCode));
                }
                //对外id
                String outerId = goodsType.getOuterId();
                if(StringUtils.isNotBlank(outerId)){
                    predicates.add(cb.equal(root.<String>get("outerId"),outerId));
                }
                
                //租户ID
                String tid = goodsType.getTenementId(); 
                if(StringUtils.isNotBlank(tid)){
                    predicates.add(cb.equal(root.<String>get("tenementId"),tid));
                }
                
                String typeUidArrStr = goodsType.getTypeUidArrStr();
                if (StringUtils.isNotBlank(typeUidArrStr)) {
                    In<String> in = cb.in(root.<String>get("parentUid"));
                    String[] split = typeUidArrStr.split(",");
                    for (String string : split) {
                        in.value(string);
                    }
                    predicates.add(in);
                }
                
                

                //未被删除 且  启用状态无视
                predicates.add(cb.equal(root.get("delFlag"),GoodsType.DEL_FLAG_EXIST));
                
                return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
            }
        };
        
    }

 

<think>嗯,用户这次想深入了解SpringDataJPA的Specification动态查询机制。从用户引用的代码片段来看,ta应该已经接触过基础用法,现在需要系统性的原理剖析和进阶指导。注意到用户特别标注"引用仅供参考,不是真正需求",说明ta真正想要的是脱离具体案例的通用性知识体系。这很符合技术学习路径——当开发者看到他人代码后,往往会渴望掌握背后的设计哲学和完整能力图谱。用户可能遇到的实际痛点是:如何优雅处理多条件动态拼接?为什么需要Root/Query/CriteriaBuilder三件套?这正是Specification的核心价值所在。ta引用的代码里那个buildSpecification方法其实就展示了解决动态拼接的经典模式——用Predicate列表收集条件,最后用cb.and合成。考虑到用户刚接触可能存在的理解障碍,我需要强调几个关键概念:1)Specification本质是函数式接口,要解释清楚(root,query,cb)->{...}这种lambda写法2)必须展开说明CriteriaAPI三剑客的分工,特别是root.get("字段")这种类型安全操作的代价3)分页组合使用的实际场景很重要,用户可能没意识到Sort和Pageable能直接和Specification配合需要特别提醒性能陷阱。比如当用户看到代码里的"cb.like(...,"%"+str+"%")"时,可能不知道这种全模糊查询在数据量大时的危害,应该主动预警。还有N+1问题——动态查询最容易忽略的就是这个,得给出JPA2.1的EntityGraph解决方案。用户没有问但肯定需要的,是如何处理复杂嵌套条件。比如OR和AND组合时,现有文档很少说明cb.or(array)这种技巧,更不会提到criteriaBuilder.and(p1,cb.or(p2,p3))的嵌套写法。这个在电商多条件筛选中是刚需。最后要预判用户的后续动作。既然掌握了动态查询,下一步必然是优化,所以提前准备SpecificationExecutor扩展方案(CustomRepository),以及QueryDSL对比建议——特别是当ta需要动态选择表字段时,Specification其实很笨重。啊,差点忘了警告常见错误!很多开发者会忘记query.distinct(true),当关联查询出现重复数据时就懵了。还有动态类型转换的陷阱,比如root.get("id").as(String.class)在字段类型不匹配时不会立即报错...</think> 下面详细介绍Spring Data JPA中Specification动态查询的使用方法,结合核心概念和实际场景说明: ### 一、Specification 基础原理 1. **核心接口定义** `Specification<T>`是函数式接口,核心方法为: ```java Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); ``` - `Root<T>`:查询的根对象(对应实体) - `CriteriaQuery`:构建查询的容器 - `CriteriaBuilder`:构造查询条件(=、>、like等) 2. **动态查询本质** 通过链式组合多个`Predicate`,实现SQL条件动态拼接,替代硬编码的JPQL语句。 --- ### 二、核心使用步骤 #### 1. 定义Specification ```java private Specification<User> buildSpec(String username, Boolean active) { return (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); // 动态条件1:用户名模糊匹配 if (StringUtils.hasText(username)) { predicates.add(cb.like(root.get("username"), "%" + username + "%")); } // 动态条件2:激活状态精确匹配 if (active != null) { predicates.add(cb.equal(root.get("active"), active)); } return cb.and(predicates.toArray(new Predicate[0])); }; } ``` #### 2. 组合查询条件 ```java // 组合多条件 Specification<User> spec = Specification.where(buildNameSpec("张")) .and(buildStatusSpec(true)) .or(buildEmailSpec("@company.com")); ``` #### 3. 执行查询 ```java // 分页+排序 Pageable pageable = PageRequest.of(0, 10, Sort.by("createTime").descending()); // 关键调用 Page<User> result = userRepository.findAll(spec, pageable); ``` --- ### 三、进阶用法 #### 1. 关联查询处理 ```java Specification<Order> orderSpec = (root, query, cb) -> { Join<Order, User> userJoin = root.join("user"); // 关联用户表 return cb.like(userJoin.get("address"), "%北京%"); }; ``` #### 2. 复杂条件组合 ```java Specification<User> complexSpec = (root, query, cb) -> { Predicate namePred = cb.like(root.get("name"), "%李%"); Predicate agePred = cb.between(root.get("age"), 18, 30); // 嵌套条件: (name LIKE '%李%') OR (age BETWEEN 18 AND 30) return cb.or(namePred, agePred); }; ``` #### 3. 动态排序 ```java Specification<User> spec = ...; List<Order> orders = new ArrayList<>(); orders.add(cb.desc(root.get("createTime"))); query.orderBy(orders); // 动态注入排序 ``` --- ### 四、性能优化技巧 1. **避免N+1问题** ```java Specification<Post> spec = (root, query, cb) -> { root.fetch("comments"); // 立即加载关联对象 return cb.isNotNull(root.get("title")); }; ``` 2. **使用Tuple减少字段** ```java Specification<User> spec = ...; CriteriaQuery<Tuple> tupleQuery = cb.createTupleQuery(); tupleQuery.multiselect(root.get("id"), root.get("name")); ``` 3. **Predicate复用** ```java static Specification<User> activeUsers() { return (root, query, cb) -> cb.isTrue(root.get("active")); } ``` --- ### 五、典型应用场景 | 场景 | 实现方案 | |---------------------|----------------------------| | 后台管理系统筛选查询 | 多字段动态组合Specification | | 报表复杂条件导出 | 动态构造统计查询条件 | | 权限数据过滤 | 注入权限谓词到Specification | > ⚠️ **注意项**: > 1. 动态拼接时使用`root.get(fieldName)`而非字符串,需确保实体字段名准确 > 2. 关联查询注意`Fetch`与`Join`的区别(`Fetch`用于加载,`Join`用于条件) > 3. 分页时避免全表`COUNT(*)`,可通过`@QueryHints`添加数据库优化提示[^1] --- **引用标识说明** [^1]: 本文引用的Specification实现模式源于Spring Data JPA官方文档及实践案例,其动态查询设计符合JPA 2.1标准规范。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值