本文不仅帮你解决错误,还帮你分析错误的原因👇👇
1. 复现错误
今天写好创建数据源
的接口,并启动项目后,通过Knife4j
调用单接口,但保出如下图错误:
于是,查看后端输出详细的错误信息,如下所示:
org.springframework.jdbc.UncategorizedSQLException:
### Error querying database. Cause: java.sql.SQLException: sql injection violation, dbType mysql, , druid-version 1.2.11, syntax error: not supported.pos 86, line 1, column 79, token IDENTIFIER deleted : SELECT * FROM `datasource` where datasource_key = ? and id <> ? and app_id = ? deleted = 0 LIMIT 1
### The error may exist in com/xxx/mapper/DatasourceMapper.java (best guess)
### The error may involve com.xxx.mapper.DatasourceMapper.getByDatasourceKeyAndId
### The error occurred while executing a query
### SQL: SELECT * FROM `datasource` where datasource_key = ? and id <> ? and app_id = ? deleted = 0 LIMIT 1
### Cause: java.sql.SQLException: sql injection violation, dbType mysql, , druid-version 1.2.11, syntax error: not supported.pos 86, line 1, column 79, token IDENTIFIER deleted : SELECT * FROM `datasource` where datasource_key = ? and id <> ? and app_id = ? deleted = 0 LIMIT 1
; uncategorized SQLException; SQL state [null]; error code [0]; sql injection violation, dbType mysql, , druid-version 1.2.11, syntax error: not supported.pos 86, line 1, column 79, token IDENTIFIER deleted : SELECT * FROM `datasource` where datasource_key = ? and id <> ? and app_id = ? deleted = 0 LIMIT 1; nested exception is java.sql.SQLException: sql injection violation, dbType mysql, , druid-version 1.2.11, syntax error: not supported.pos 86, line 1, column 79, token IDENTIFIER deleted : SELECT * FROM `datasource` where datasource_key = ? and id <> ? and app_id = ? deleted = 0 LIMIT 1
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
at com.sun.proxy.$Proxy160.selectOne(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:160)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:89)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
at com.sun.proxy.$Proxy166.getByDatasourceKeyAndId(Unknown Source)
at com.xxx.service.DatasourceService.modifyDatasource(DatasourceService.java:104)
at com.xxx.service.DatasourceService$$FastClassBySpringCGLIB$$ae4aeefa.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.xxx.service.DatasourceService$$EnhancerBySpringCGLIB$$1c1c65fc.modifyDatasource(<generated>)
at com.xxx.controller.DatasourceController.modifyDatasource(DatasourceController.java:96)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at com.github.xiaoymin.knife4j.spring.filter.SecurityBasicAuthFilter.doFilter(SecurityBasicAuthFilter.java:87)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:114)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
.....
由于篇幅有限,没有粘贴出全部的错误信息。
2. 分析错误
由于错误信息很多,我们只需要关注Cause: java.sql.SQLException: sql injection violation, dbType mysql, , druid-version 1.2.11, syntax error: not supported.pos 86, line 1, column 79, token IDENTIFIER deleted
这句话即可。
正赶上最近ChatGPT
比较火,我们可以借助它来分析我的错误,如下图所示:
ChatGPT
说我的错误由于SQL
注入导致的,使用了mysql
不支持的语法。
于是继续分析上述的错误信息,查找到DatasourceMapper
类中的getByDatasourceKeyAndId
方法,该方法如下代码所示:
@Select(
"SELECT "
+ " * "
+ "FROM "
+ " `datasource` "
+ "WHERE "
+ " datasource_key = #{datasourceKey} and id <> #{id} and app_id = #{appId} deleted = 0 LIMIT 1")
Datasource getByDatasourceKeyAndId(String datasourceKey, Long id, Long appId);
经过反复检查上述代码,却没有发现任何问题,只能通过如下断点的方式分析:
通过断点发现,在app_id = ? deleted = 0
没有and
符号,即app_id = #{appId} deleted = 0
没有and
符号。
3. 解决错误
既然在app_id = #{appId} deleted = 0
没有and
符号,在其加上and
符号即可,如下代码所示:
@Select(
"SELECT "
+ " * "
+ "FROM "
+ " `datasource` "
+ "where "
+ " datasource_key = #{datasourceKey} and id <> #{id} and app_id = #{appId} and deleted = 0 LIMIT 1")
Datasource getByDatasourceKeyAndId(String datasourceKey, Long id, Long appId);
重新启动项目,再次使用Knife4j
测试该接口,即可成功访问,如下图所示:
4. 解决该错误的其他方法
\
4.1 方法说明
我的错误是由于缺少关键字(and
)导致的。
如果我的错误解决方法,无法解决你的错误,可以参考如下的解决方法👇👇
一般情况下,该错误是因为表字段和关键字
冲突,这个问题解决后多数情况都正常了。
若开发工具不显示SQL
的关键字,导致不方便区分的话,我知道如下三种解决办法:
-
可以把感觉不对劲的表名和字段名都用 ` ` 给注上,如 `status`。
-
给冲突的字段或表名起别名。
-
还可以在
oracle
中使用双引号" "
,将冲突的列名括起来。
4.2 建表规范
为了避免表字段和关键字
冲突,在数据库最初建表时,需要有意识的避免字段冲突问题,下面是网上找的建表命名规范:
-
采用
系统名+_+t_+模块名+_+表义名
格式构成。 -
若数据库中只含有单个模块,命名可采用
系统名+t_+表义名
格式构成。 -
整个表名的长度不要超过
30
个字符。 -
系统名、模块名均采用小写字符。
-
模块名或表义名均以其英文单词命名,且字符间不加分割符:
-
表义名中单词的首字符大写,其它字符小写。
-
多个单词间也不加任何分割符,单词全部采用单数形式。
-
-
表别名命名规则:
-
取表义名的
前3个字符
加最后一个字符。 -
如果存在冲突,适当增加字符(如取表义名的
前4个字符
加最后一个字符等)。
-
-
关联表命名为
Re_表A_表B
:-
Re
是Relative
的缩写 -
表A
和表B
均采用其表义名或缩写形式。
-
这样会避免与关键字冲突,将麻烦降到最低。
5. 补充说明
如果你对Knife4j感兴趣,可以参考博文:全网最全的Knife4j swaggerj介绍