如何在ReactiveCrudRepository的命名查询中正确设置表名?
报错原因
- 标准的SQL参数绑定只能用来传递查询值(比如where条件里的name值),不能用来传递表名、列名这类SQL标识符。你传入的表名参数会被当成字符串加引号包裹,生成
delete from 'user_table' where name = ?这类非法语法的SQL,所以执行报错。 - 你贴的代码本身还有语法错误:
@Param注解的参数名需要加引号,且delete方法末尾缺少右括号。
解决方案1:使用Spring Expression Language(SpEL)实现动态表名
Spring Data的@Query注解支持通过SpEL解析动态传入的标识符,修改代码如下:
@Repository public interface DeleteRepo extends ReactiveCrudRepository<Emp,String>{ String query = "delete from #{#table1} where name = :name"; @Query(query) Mono<Integer> delete(@Param("name") String name, @Param("table1") String table1); }
这里#{#table1}会在SQL执行前直接把参数值作为标识符拼入SQL,不会加引号包裹,符合语法要求。
解决方案2:自定义Repository实现拼接SQL
如果SpEL方案不适用(比如低版本Spring Data不兼容),可以通过R2dbcEntityTemplate手动拼接执行SQL,要额外做好SQL注入防护:
首先定义自定义接口:
public interface CustomDeleteRepo { Mono<Integer> deleteByTableNameAndName(String tableName, String name); }
然后实现接口:
@Repository public class CustomDeleteRepoImpl implements CustomDeleteRepo { private final R2dbcEntityTemplate template; public CustomDeleteRepoImpl(R2dbcEntityTemplate template) { this.template = template; } @Override public Mono<Integer> deleteByTableNameAndName(String tableName, String name) { // 必须加校验:只允许合法的表名,防止SQL注入 if (!List.of("emp", "dept", "user").contains(tableName)) { return Mono.error(new IllegalArgumentException("非法表名")); } String sql = String.format("delete from %s where name = :name", tableName); return template.getDatabaseClient() .sql(sql) .bind("name", name) .fetch() .rowsUpdated(); } }
最后让你的DeleteRepo继承这个自定义接口即可:
public interface DeleteRepo extends ReactiveCrudRepository<Emp,String>, CustomDeleteRepo { }
安全提示
动态拼接表名存在SQL注入风险,不管用哪种方案,都必须对传入的表名参数做合法性校验,仅允许传入预设的合法表名,禁止直接透传用户传入的未校验值作为表名。
内容的提问来源于stack exchange,提问作者Pkdrc
相关产品推荐
相关产品推荐

