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);
```
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值