是否存在对标Bean Validation的注解式动态查询生成方案?
是否存在对标Bean Validation的注解式动态查询生成方案?
作为常年跟业务代码打交道的开发者,估计不少人都跟我一样,发现参数校验和动态查询构造的代码逻辑简直如出一辙——都是围绕对象的属性做一堆非空/非空串判断,然后执行对应的逻辑,写多了真的是又冗余又容易漏。
先给大家举个直观的例子:
首先定义一个简单的User实体类:
public class User { private String name; private Integer age; // getters and setters }
手动参数校验的代码
public void validate(User user) { if (user.getName() == null || user.getName().isBlank()) { throw new IllegalArgumentException("name can not be empty"); } if (user.getAge() == null) { throw new IllegalArgumentException("age can not be empty"); } if (user.getAge() < 18 || user.getAge() > 60) { throw new IllegalArgumentException("age should be between 18 and 60"); } // business logic }
动态查询的代码(两种常见写法)
第一种是用StringJoiner拼接SQL条件:
public static String buildWhere(UserQuery query, List<Object> argList) { StringJoiner where = new StringJoiner(" AND ", " WHERE ", " "); where.setEmptyValue(" "); if (query.getName() != null && !query.getName().isBlank()) { where.add("name LIKE ?"); argList.add(query.getName()); } if (query.getAge() != null) { where.add("age = ?"); argList.add(query.getAge()); } return where.toString(); }
第二种是用JPA的Specification:
public List<User> query(User query) { Specification<User> spec = (root, cq, cb) -> { List<Predicate> predicates = new ArrayList<>(); if (query.getName() != null && !query.getNameLike().isBlank()) { predicates.add(cb.like(root.get("name"), " % " + query.getName() + " % ")); } if (query.getAge() != null) { predicates.add(cb.eq(root.get("age"), query.getAge())); } //... return cb.and(predicates.toArray(new Predicate[0])); }; return userRepository.findAll(spec); }
你看,这些代码是不是长得几乎一模一样?都是重复的判断逻辑,只是后续执行的操作不同——校验是抛异常,查询是拼接条件。
而参数校验这块,我们早就有了**Jakarta Validation(原Bean Validation)**这种声明式的解决方案,只要给实体字段加注解就行,再也不用写一堆if:
public class User { @NotBlank private String name; @NotNull @Range(min = 18, max = 60) private Integer age; // getters and setters }
那问题来了:有没有类似的注解式方案,能让我们用注解来定义动态查询的条件,比如像下面这样写查询参数类?
public class UserQuery { @Condition("name LIKE ?") private String name; @Condition("age = ?") private Integer age; }
其实这类需求早就有成熟的实现了,很多ORM框架或者第三方库都提供了类似的注解式动态查询支持:
- 比如MyBatis-Plus的
@TableField配合LambdaQueryWrapper,或者它的@Query注解; - JPA生态里也有Querydsl、Spring Data JPA的
@Query配合SpEL表达式,甚至一些第三方扩展库(比如Blaze-Persistence)提供了更丰富的注解式查询定义; - 还有一些轻量的工具库,专门做注解式动态SQL生成,通过自定义注解(比如你示例里的
@Condition)配合反射来自动拼接查询条件。
核心思路都是跟Bean Validation一致:用注解声明规则,框架通过反射解析注解,自动执行对应的逻辑(这里是生成查询条件),从而避免重复的if判断代码,让代码更简洁易维护。
内容来源于stack exchange
相关产品推荐
相关产品推荐

