一些奇奇怪怪的问题

背景

在帮朋友开发的一个项目中,用了 Mybatis-plus作为 ORM框架,在一个简单的查询在执行的时候一直在报错,困惑了一整个小时,最后才发现是因为一个字段名的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Wrapped by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: SELECT  id,model_code,prompt_id,version_no,release_flag,default_flag,prompt_content,batch_recording_file_id,batch_recording_file_content,output,is_delete,tenant_id,create_dept,create_by,create_time,update_by,update_time  FROM ai_prompt_debug_log      WHERE  (prompt_id = ?)
at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:39) ~[mybatis-plus-core-3.5.8.jar!/:3.5.8]
at com.baomidou.mybatisplus.extension.parser.JsqlParserSupport.parserSingle(JsqlParserSupport.java:51) ~[mybatis-plus-extension-3.5.8.jar!/:3.5.8]
at com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.beforeQuery(TenantLineInnerInterceptor.java:67) ~[mybatis-plus-extension-3.5.8.jar!/:3.5.8]
at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:78) ~[mybatis-plus-extension-3.5.8.jar!/:3.5.8]
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:59) ~[mybatis-3.5.16.jar!/:3.5.16]
at jdk.proxy2/jdk.proxy2.$Proxy251.query(Unknown Source) ~[na:na]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154) ~[mybatis-3.5.16.jar!/:3.5.16]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147) ~[mybatis-3.5.16.jar!/:3.5.16]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:142) ~[mybatis-3.5.16.jar!/:3.5.16]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:333) ~[mybatis-spring-3.0.4.jar!/:3.0.4]
... 112 common frames omitted

刚看到这个错误的时候感觉莫名其妙的,这么简单的一个查询,还能整出这个幺蛾子,就很奇怪了

问题分析

根据多年经验第一反应就是懵逼,再仔细看一下堆栈信息,感觉可能是这里面存在某些关键字导致的,但是看了每个字段,发现没有什么异常的地方,先顺着这个思路去验证一下

问题排查

  • 根据堆栈信息使用发现问题出现在了JsqlParserSupport.parserSingle这个方法
  • 因为JsqlParserSupport是一个抽象类,看日志信息实际使用的类是TenantLineInnerInterceptor,那就弄个测试类,去用相关的参数调试一下
  • 因为怀疑是关键字引起的,采取的方法是,一个字段一个字段删除,直到查询正常为止。
  • 当我删除掉output时候,结果正常了,说明output这个字段名是一个关键字,导致了查询失败。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.apache.commons.compress.utils.Lists;
import org.dromara.common.tenant.handle.PlusTenantLineHandler;
import org.dromara.common.tenant.properties.TenantProperties;

public class Test {
public static void main(String[] args) {
final TenantProperties tenantProperties = new TenantProperties();
tenantProperties.setEnable(true);
tenantProperties.setExcludes(Lists.newArrayList());
PlusTenantLineHandler tenantLineHandler = new PlusTenantLineHandler(tenantProperties);
TenantLineInnerInterceptor tenantLineInnerInterceptor = new TenantLineInnerInterceptor(tenantLineHandler);
tenantLineInnerInterceptor.parserSingle("SELECT id,model_code,prompt_id,version_no,release_flag,default_flag,prompt_content,is_delete,tenant_id,create_dept,create_by,create_time,update_by,update_time " +
"FROM ai_prompt_debug_log WHERE (prompt_id = ?)" ,
"123"
);
}
}

删除掉output字段后,日志显示正常,不再报错了,说明output这个字段名是一个关键字,导致了查询失败,既然找到了问题,就可以开始解决它。

解决办法

  • 因为output是一个关键字,所以在查询的时候需要用反引号括起来
  • 或者在表设计中将字段名改非关键字,比如 output_text

总结

问题根源:关键字冲突与SQL解析失败

  • output是SQL保留字:在许多数据库(如SQL Server)中,OUTPUT是一个关键命令,用于输出结果或操作影响的行。当 MyBatis-Plus 的 SQL 解析器(如 JSqlParser)遇到这个单词时,会试图按照其语法规则去理解,但它在您的上下文中是一个字段名,这导致了语法解析混乱。
  • 多租户插件是触发点:从您的错误堆栈看,问题发生在 TenantLineInnerInterceptor(多租户插件)处理 SQL 的阶段。这个插件需要解析原始SQL,以便自动注入租户ID条件。在解析到 output时,解析器失败了,从而抛出了异常。

最佳实践与根本解决策略

  • 在数据库设计阶段,尽量避免使用任何数据库的保留字作为表名或字段名。可以考虑将 output改为 output_info, output_result, output_content等非关键字,可以从根本上杜绝此类问题。
  • 对于关键字使用*``*括起来,避免一些意外的错误

写在结尾

这个关键字引发的问题,浪费了我不少时间,希望能提醒其他开发者,避免类似的问题。大家还知道有哪些关键字会导致类似的问题吗?