Spring WebFlux+R2DBC应用中如何使用RSQL查询?
在Spring WebFlux + R2DBC应用中使用RSQL查询的实现方案
核心说明
JPA的Specification是为ORM抽象设计的,和R2DBC基于原生SQL构建的Condition(包含Comparison子类)体系完全不兼容,没法直接转换。正确路径是从RSQL解析阶段直接生成R2DBC可用的SQL条件对象。
具体实现步骤
1. 引入必要依赖
添加RSQL解析库和Spring Data R2DBC依赖(已引入可跳过):
<!-- RSQL解析器 --> <dependency> <groupId>cz.jirutka.rsql</groupId> <artifactId>rsql-parser</artifactId> <version>2.1.0</version> </dependency> <!-- Spring Data R2DBC --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-r2dbc</artifactId> <version>3.2.0</version> </dependency>
2. 自定义RSQL Visitor生成R2DBC Condition
实现RSQLVisitor接口,将RSQL语法节点直接解析为R2DBC的Condition对象(支持单一条件Comparison和组合条件AndCondition/OrCondition):
import cz.jirutka.rsql.parser.ast.*; import org.springframework.data.relational.core.sql.*; import java.util.List; import java.util.stream.Collectors; public class RsqlToR2dbcConditionVisitor implements RSQLVisitor<Condition, Object> { @Override public Condition visit(ComparisonNode node, Object param) { SqlIdentifier column = SqlIdentifier.unquoted(node.getSelector()); List<String> args = node.getArguments(); return switch (node.getOperator()) { case EQUAL -> Conditions.isEqualTo(column, args.get(0)); case NOT_EQUAL -> Conditions.isNotEqualTo(column, args.get(0)); case GREATER_THAN -> Conditions.greaterThan(column, args.get(0)); case GREATER_THAN_OR_EQUAL -> Conditions.greaterThanOrEqualTo(column, args.get(0)); case LESS_THAN -> Conditions.lessThan(column, args.get(0)); case LESS_THAN_OR_EQUAL -> Conditions.lessThanOrEqualTo(column, args.get(0)); case IN -> Conditions.in(column, args.toArray()); case NOT_IN -> Conditions.notIn(column, args.toArray()); default -> throw new IllegalArgumentException("不支持的RSQL操作符: " + node.getOperator()); }; } @Override public Condition visit(LogicalNode node, Object param) { List<Condition> conditions = node.getChildren() .stream() .map(child -> child.accept(this, param)) .collect(Collectors.toList()); return switch (node.getOperator()) { case AND -> Conditions.and(conditions); case OR -> Conditions.or(conditions); default -> throw new IllegalArgumentException("不支持的逻辑操作符: " + node.getOperator()); }; } @Override public Condition visit(Node node, Object param) { return node.accept(this, param); } }
3. 整合到数据查询层
利用R2dbcEntityTemplate执行解析后的条件查询,无需手动拼接SQL:
import cz.jirutka.rsql.parser.RSQLParser; import cz.jirutka.rsql.parser.ast.Node; import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.core.sql.Condition; import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; import reactor.core.publisher.Flux; @Service public class UserService { private final R2dbcEntityTemplate entityTemplate; private final RSQLParser rsqlParser = new RSQLParser(); private final RsqlToR2dbcConditionVisitor rsqlVisitor = new RsqlToR2dbcConditionVisitor(); public UserService(R2dbcEntityTemplate entityTemplate) { this.entityTemplate = entityTemplate; } public Flux<User> findByRsql(String rsql) { // 解析RSQL字符串为语法节点 Node rsqlNode = rsqlParser.parse(rsql); // 转换为R2DBC Condition Condition condition = rsqlNode.accept(rsqlVisitor, null); // 执行查询 return entityTemplate.select(User.class) .matching(Query.query(condition)) .all(); } }
4. 控制器层接收RSQL参数
在WebFlux端点中接收URL参数里的RSQL字符串,调用查询逻辑:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController @RequestMapping("/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping public Flux<User> getUsers(@RequestParam String rsql) { return userService.findByRsql(rsql); } }
补充提示
- 上述示例简化了类型转换逻辑,实际场景中需根据字段类型(如数字、日期)对RSQL参数做类型转换,避免SQL类型不匹配。
- 利用
R2dbcEntityTemplate的查询方式会自动处理参数绑定,无需担心SQL注入风险。
内容的提问来源于stack exchange,提问作者Thomas Dons
相关产品推荐
相关产品推荐

