Angular 2 + Spring Boot多动态参数(部分可空)搜索实现求助
嗨,这个场景我太熟悉了!当你需要处理多条件动态搜索,而且部分参数可能为空的时候,Spring Data JPA有几个非常实用的方案,我结合你的Angular+Spring Boot场景给你详细拆解:
这个方案是Spring自带的,不需要额外引入依赖,灵活性拉满,完全适配你的动态参数需求。
步骤如下:
扩展你的Repository接口
让你的UserRepository同时继承JpaSpecificationExecutor<User>,这样才能使用Specification能力:public interface UserRepository extends CrudRepository<User, Long>, JpaSpecificationExecutor<User> { }构建动态查询条件
在你的Service层写一个方法,根据传入的参数(姓名、年龄、城市、国家)来判断哪些参数不为空,然后逐个添加查询条件:@Service public class UserService { @Autowired private UserRepository userRepository; public List<User> searchUsers(String name, Integer age, String city, String country) { Specification<User> spec = (root, query, criteriaBuilder) -> { List<Predicate> predicates = new ArrayList<>(); // 姓名不为空且非空字符串时添加条件 if (name != null && !name.trim().isEmpty()) { predicates.add(criteriaBuilder.equal(root.get("name"), name.trim())); } // 年龄不为空时添加条件 if (age != null) { predicates.add(criteriaBuilder.equal(root.get("age"), age)); } // 城市不为空且非空字符串时添加条件 if (city != null && !city.trim().isEmpty()) { predicates.add(criteriaBuilder.equal(root.get("city"), city.trim())); } // 国家不为空且非空字符串时添加条件 if (country != null && !country.trim().isEmpty()) { predicates.add(criteriaBuilder.equal(root.get("country"), country.trim())); } // 把所有条件用AND连接起来,只返回符合所有非空参数的用户 return criteriaBuilder.and(predicates.toArray(new Predicate[0])); }; return userRepository.findAll(spec); } }编写RestController接口
接收前端传过来的参数(注意设置required = false,因为参数可能为空):@RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; @GetMapping("/search") public ResponseEntity<List<User>> searchUsers( @RequestParam(required = false) String name, @RequestParam(required = false) Integer age, @RequestParam(required = false) String city, @RequestParam(required = false) String country) { List<User> users = userService.searchUsers(name, age, city, country); return ResponseEntity.ok(users); } }前端Angular处理
调用接口的时候,只把有值的参数传到后端就行,比如:searchUsers(name: string, age: number, city: string, country: string): Observable<User[]> { let params = new HttpParams(); if (name?.trim()) params = params.set('name', name.trim()); if (age) params = params.set('age', age.toString()); if (city?.trim()) params = params.set('city', city.trim()); if (country?.trim()) params = params.set('country', country.trim()); return this.http.get<User[]>('/api/users/search', { params }); }
如果你的项目比较复杂,参数很多,QueryDSL会让你的代码更简洁易读,但需要额外配置依赖。
添加QueryDSL依赖(Maven为例)
在pom.xml里加入:<dependencies> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> </plugins> </build>执行
mvn compile后,会自动生成QUser类(对应你的User实体)。扩展Repository接口
让UserRepository继承QuerydslPredicateExecutor<User>:public interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> { }构建查询条件
在Service层用BooleanBuilder来拼接条件:@Service public class UserService { @Autowired private UserRepository userRepository; public Iterable<User> searchUsers(String name, Integer age, String city, String country) { QUser qUser = QUser.user; BooleanBuilder builder = new BooleanBuilder(); if (name != null && !name.trim().isEmpty()) { builder.and(qUser.name.eq(name.trim())); } if (age != null) { builder.and(qUser.age.eq(age)); } if (city != null && !city.trim().isEmpty()) { builder.and(qUser.city.eq(city.trim())); } if (country != null && !country.trim().isEmpty()) { builder.and(qUser.country.eq(country.trim())); } return userRepository.findAll(builder); } }这个写法比Specification更直观,代码量也更少。
如果你的搜索条件不多,也可以直接写JPQL查询,不过要注意处理空参数的情况:
在Repository里定义查询方法
public interface UserRepository extends CrudRepository<User, Long> { @Query("SELECT u FROM User u WHERE " + "(?1 IS NULL OR u.name = ?1) AND " + "(?2 IS NULL OR u.age = ?2) AND " + "(?3 IS NULL OR u.city = ?3) AND " + "(?4 IS NULL OR u.country = ?4)") List<User> searchUsers(String name, Integer age, String city, String country); }处理空字符串
因为前端可能传空字符串(比如搜索框没填但提交了),所以在Service层要把空字符串转成null,不然JPQL的条件会失效:@Service public class UserService { @Autowired private UserRepository userRepository; public List<User> searchUsers(String name, Integer age, String city, String country) { // 把空字符串转为null if (name != null && name.trim().isEmpty()) name = null; if (city != null && city.trim().isEmpty()) city = null; if (country != null && country.trim().isEmpty()) country = null; return userRepository.searchUsers(name, age, city, country); } }这个方法简单,但如果参数多了,JPQL会很长,维护起来比较麻烦。
个人推荐你用方案一(Specification),不需要额外依赖,Spring原生支持,足够应对你的场景;如果以后参数变多,再考虑切换到QueryDSL。
内容的提问来源于stack exchange,提问作者rahul sharma

