You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:43:10