Java中避免REST API搜索方法复杂if-else分支的最优方案
嘿,这个场景我太熟了——多条件搜索里一堆null判断和嵌套if-else,时间长了代码简直像“意大利面”,维护起来头疼得很。下面给你几个实用的优化方案,从简单到进阶,你可以根据项目场景选:
方案1:建造者模式+JPA Specification(最推荐)
这个方案既能优雅封装查询参数,又能动态拼接查询条件,彻底告别冗余的if分支。
首先定义一个查询参数的封装类,用建造者模式来构建:
public class PersonSearchCriteria { private String name; private String city; private String state; private String code; // 私有构造,只能通过Builder创建 private PersonSearchCriteria(Builder builder) { this.name = builder.name; this.city = builder.city; this.state = builder.state; this.code = builder.code; } // 生成所有字段的getter方法 public String getName() { return name; } public String getCity() { return city; } public String getState() { return state; } public String getCode() { return code; } // 建造者内部类 public static class Builder { private String name; private String city; private String state; private String code; public Builder name(String name) { this.name = name; return this; } public Builder city(String city) { this.city = city; return this; } public Builder state(String state) { this.state = state; return this; } public Builder code(String code) { this.code = code; return this; } public PersonSearchCriteria build() { return new PersonSearchCriteria(this); } } }
然后用JPA的Specification动态构建查询条件,自动过滤空值:
public List<Person> searchPerson(PersonSearchCriteria criteria) { Specification<Person> spec = (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); // 只给非空参数添加查询条件 if (criteria.getName() != null) { predicates.add(cb.equal(root.get("name"), criteria.getName())); } if (criteria.getCity() != null) { predicates.add(cb.equal(root.get("city"), criteria.getCity())); } if (criteria.getState() != null) { predicates.add(cb.equal(root.get("state"), criteria.getState())); } if (criteria.getCode() != null) { predicates.add(cb.equal(root.get("code"), criteria.getCode())); } // 把所有条件用AND拼接 return cb.and(predicates.toArray(new Predicate[0])); }; return personRepository.findAll(spec); }
调用的时候也很优雅:
// 比如只传name和city PersonSearchCriteria criteria = new PersonSearchCriteria.Builder() .name("张三") .city("北京") .build(); List<Person> result = searchPerson(criteria);
优势:参数结构清晰,查询条件动态生成,扩展性极强——以后加新的查询参数,只需要在PersonSearchCriteria和Builder里加字段,再添一行predicate即可,完全不用改原有逻辑。
方案2:用Map统一封装参数(灵活轻量)
如果你的查询参数经常变动,或者不想写额外的实体类,可以用Map来接收参数,然后遍历处理非空值:
public List<Person> searchPerson(Map<String, String> params) { Specification<Person> spec = (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); // 遍历参数,只处理非空值 params.forEach((key, value) -> { if (value != null && !value.trim().isEmpty()) { predicates.add(cb.equal(root.get(key), value)); } }); return cb.and(predicates.toArray(new Predicate[0])); }; return personRepository.findAll(spec); }
注意:这里的Map的key要和Person实体类的字段名严格对应,不然会抛出字段不存在的异常。如果需要做模糊查询,只需要把cb.equal换成cb.like就行,非常灵活。
方案3:Spring Data查询推导+Optional(简单场景适用)
如果你的查询组合比较固定,不想写复杂的Specification,可以用Spring Data的查询推导功能,结合Optional自动处理空值:
首先在Repository接口里定义方法:
public interface PersonRepository extends JpaRepository<Person, Long> { List<Person> findByNameAndCityAndStateAndCode( Optional<String> name, Optional<String> city, Optional<String> state, Optional<String> code ); }
然后业务层调用:
public List<Person> searchPerson(String name, String city, String state, String code) { return personRepository.findByNameAndCityAndStateAndCode( Optional.ofNullable(name), Optional.ofNullable(city), Optional.ofNullable(state), Optional.ofNullable(code) ); }
优势:代码极简,不用自己写查询逻辑,Spring Data会自动生成对应的SQL。但缺点也很明显——如果需要支持多种参数组合(比如只查name+state,或者city+code),就需要在Repository里定义大量方法,扩展性差,适合简单的固定组合场景。
内容的提问来源于stack exchange,提问作者Viraj

