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