webflux使用r2dbc-mysql时动态sql的实现方案

### 1、 问题描述

ReactiveCrudRepository 提供了简单的crud, 同时也提供了类似 select...By.. query...By..等驼峰式的方法简单查询,但是业务开发对于mysql等关系型数据库连表、动态查询条件是普遍的。 \
就目前翻阅官网等没有找到合适的解决方法, 故从源码入手修改适应业务的方案。

### 2、源码解析
```
1、跳到 ReactiveCrudRepository 接口声明处 可以看到提供的默认方法
@NoRepositoryBean
public interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> Mono<S> save(S entity);

    <S extends T> Flux<S> saveAll(Iterable<S> entities);
    
2、 ctrl+alt 单击跳转进去
具体的实现类是 SimpleR2dbcRepository
这里其实看不出自定义的方法是怎么实现的,主要是r2dbc使用了代理来操作

3、 核心由 DefaultDatabaseClient 的 execute 执行
private <T> FetchSpec<T> execute(Supplier<String> sqlSupplier, BiFunction<Row, RowMetadata, T> mappingFunction) {
   String sql = getRequiredSql(sqlSupplier);
......


// 这一步是把@Query 声明的sql 变量替换成? 这样的jdbc老传统模式
         PreparedOperation<String> operation = DefaultDatabaseClient.this.namedParameterExpander.expand(
               sql, DefaultDatabaseClient.this.bindMarkersFactory, namedBindings);

         String expanded = getRequiredSql(operation);
         if (logger.isTraceEnabled()) {
            logger.trace("Expanded SQL [" + expanded + "]");
         }
// 下面这句是不是很是熟悉
         Statement statement = connection.createStatement(expanded);
         BindTarget bindTarget = new StatementWrapper(statement);
         ....
   };

4、进到expand去看看

public PreparedOperation<String> expand(
      String sql, BindMarkersFactory bindMarkersFactory, BindParameterSource paramSource) {

   ParsedSql parsedSql = getParsedSql(sql);
   // 最终处理函数是 substituteNamedParameters
   return NamedParameterUtils.substituteNamedParameters(parsedSql, bindMarkersFactory, paramSource);
}

5、修改点
进入substituteNamedParameters
for (int i = 0; i < paramNames.size(); i++) {
            .....
            if (paramSource.hasValue(paramName)) {
                      ......
//                    占位替换 把约定以sw_开头的变量直接拼接sql 而不是拼接? 占位符
                } else if (paramName.startsWith("sw_")) {
                    actualSql.append(paramSource.getValue(paramName));
                } else {
                    actualSql.append(marker.getPlaceholder());
                }
            }
            else {
                actualSql.append(marker.getPlaceholder());
            }
            lastIndex = endIndex;
        }
```
### 3、修改技巧
- 项目中新建包 org.springframework.r2dbc.core 及 NamedParameterUtils 类
- 把NamedParameterUtils的源码cp进去按照修改点修改

### 4、修改后使用方式

```
根据请求动态配置条件
Criteria criteria = where("tt.tenant_id").is(userVO.getTenantId());
if(statuz!=-1){
    criteria = criteria.and("tt.statuz").is(statuz);
}
if(!"30100".equals(taskType)){
    criteria = criteria.and("tt.task_type").is(taskType);
}
if(StringUtils.hasText(keyword)){
    criteria = criteria.and("tg.gateway_name").like("%"+keyword+"%");
}
String whereCondition = criteria.toString();
taskRepository.findAll();
Mono<Long> total = taskRepository.countRecordNum(whereCondition);

......
声明的占位变量有sw_开头即可,这样就能满足动态条件的要求了
@Query("select " +
        "count(1) " +
        "from t_task tt " +
        "left join t_gateway tg on (tg.gateway_id = tt.gateway_id) " +
        "left join t_dictionary tsd on (tsd.dict_code = tt.task_type) " +
        "left join t_user tsu on(tsu.user_id=tt.create_by and tsu.tenant_id=tt.tenant_id) " +
        "where :sw_condition"
        )
Mono<Long> countRecordNum(String sw_condition);
```
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Spring WebFlux使用R2DBC连接MySQL并集成Flyway,可以按照以下步骤进行操作: 1. 在pom.xml中添加所需的依赖项: ```xml <!-- Spring WebFlux --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <!-- R2DBC MySQL 驱动 --> <dependency> <groupId>dev.miku</groupId> <artifactId>r2dbc-mysql</artifactId> <version>0.8.2.RELEASE</version> </dependency> <!-- Flyway --> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency> ``` 2. 在application.properties中配置R2DBC连接和Flyway: ```properties # R2DBC MySQL 连接配置 spring.r2dbc.url=r2dbc:mysql://localhost:3306/test spring.r2dbc.username=root spring.r2dbc.password=root # Flyway 配置 flyway.locations=classpath:db/migration flyway.clean-disabled=false flyway.baseline-on-migrate=true ``` 3. 创建数据库迁移脚本文件,存放在`src/main/resources/db/migration`目录下。例如,创建一个名为`V1__init.sql`的脚本文件,用于初始化数据库: ```sql CREATE TABLE `user` ( `id` INT PRIMARY KEY, `name` VARCHAR(50) NOT NULL, `age` INT NOT NULL ); ``` 4. 在Spring Boot应用程序中创建一个数据访问对象(DAO)来处理与数据库的交互。可以使用R2DBC提供的`DatabaseClient`或Spring Data R2DBC来简化数据库访问。 5. 启动应用程序,Flyway将自动执行数据库迁移脚本,初始化数据库。 注意:R2DBC是非阻塞的数据库访问方式,与传统的JDBC和Spring Data JPA不同,需要使用异步的方式进行操作。确保代码中的异步操作正确处理。 希望对你有所帮助!如有其他问题,请随提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值