简介

最近开发在springt ORM框架下遇到一个以下报错信息。我做了一个复现

"C:\Program Files\Java\jdk-1.8\bin\java.exe" "-javaagent:E:\idea\IntelliJ IDEA 2024.1.3\lib\idea_rt.jar=50160:E:\idea\IntelliJ IDEA 2024.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk-1.8\jre\lib\charsets.jar;C:\Program Files\Java\jdk-1.8\jre\lib\deploy.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk-1.8\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk-1.8\jre\lib\javaws.jar;C:\Program Files\Java\jdk-1.8\jre\lib\jce.jar;C:\Program Files\Java\jdk-1.8\jre\lib\jfr.jar;C:\Program Files\Java\jdk-1.8\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk-1.8\jre\lib\jsse.jar;C:\Program Files\Java\jdk-1.8\jre\lib\management-agent.jar;C:\Program Files\Java\jdk-1.8\jre\lib\plugin.jar;C:\Program Files\Java\jdk-1.8\jre\lib\resources.jar;C:\Program Files\Java\jdk-1.8\jre\lib\rt.jar;F:\中邮消费\pgroortext\target\classes;C:\Users\55055\.m2\repository\org\postgresql\postgresql\42.7.5\postgresql-42.7.5.jar;C:\Users\55055\.m2\repository\org\checkerframework\checker-qual\3.48.3\checker-qual-3.48.3.jar;C:\Users\55055\.m2\repository\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;C:\Users\55055\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\55055\.m2\repository\com\alibaba\druid\1.2.11\druid-1.2.11.jar;C:\Users\55055\.m2\repository\com\oracle\database\jdbc\ojdbc8\19.8.0.0\ojdbc8-19.8.0.0.jar;C:\Users\55055\.m2\repository\org\jetbrains\annotations\17.0.0\annotations-17.0.0.jar" org.StringTpyeDemo
Exception in thread "main" org.postgresql.util.PSQLException: 错误: 操作符不存在: integer = character varying
  建议:没有匹配指定名称和参数类型的操作符. 您也许需要增加明确的类型转换.
  位置:31
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2733)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2420)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372)
	at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:517)
	at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:434)
	at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:194)
	at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:137)
	at org.SimpleOrm.select(SimpleOrm.java:19)
	at org.StringTpyeDemo.main(StringTpyeDemo.java:18)

Process finished with exit code 1

SQL就是简单的查询,采用了预备语句的绑定变量。

结论

直接给大家说结论吧,这个问题主要是由于JDBC中stringtype参数,默认会在预备语句中注入参数时,对于string类型的参数会使用setString进行转换之后再填充到预备语句中,传入到PG中的SQL就会变成一个显示类型转换,PG这里接收到显性类型转换的表达式时,优化器便不会再进行隐士转换,便会出现报错。官网上对于该参数的解释。(默认情况下表ID字段为small、bigint优化器会自动执行隐式转换,intger不会,后续文章跟进)。

在这里插入图片描述

Specify the type to use when binding PreparedStatement parameters set via setString()

示例


postgres=# \dSt+   t_json 
                                               Table "public.t_json"
 Column |       Type        | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+-------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer           |           |          |         | plain    |             |              | 
 data   | character varying |           |          |         | extended |             |              | 
Access method: heap

postgres=# SELECT *  FROM t_json WHERE ID = '1';
 id | data 
----+------
  1 | 2
  1 | 2
  1 | 2
  1 | 2
(4 rows)

postgres=# SELECT *  FROM t_json WHERE ID = CAST('1' AS VARCHAR);
ERROR:  operator does not exist: integer = character varying
LINE 1: SELECT *  FROM t_json WHERE ID = CAST('1' AS VARCHAR);
                                       ^
HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
postgres=# SELECT *  FROM t_json WHERE ID = '1' :: VARCHAR;
ERROR:  operator does not exist: integer = character varying
LINE 1: SELECT *  FROM t_json WHERE ID = '1' :: VARCHAR;
                                       ^
HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.

以上情况吧,如果执行SQL报错,PG不会记录参数的具体值,所以你只能看到
$1$2 之类的情况。

所以以上问题,排查起来是相当麻烦

抓包获取详细情况

通过抓包的方式俘获预备SQL的绑定参数值

## 更新一下yum可用仓库
bash <(curl -sSL https://linuxmirrors.cn/main.sh)

 yum clean all
 yum makecache

## 下载工具包
yum install -y wireshark
tshark -v


sudo tshark -i ens33 -f "tcp port 5432"   -Y 'pgsql.query or pgsql.parameter_value'   -T fields   -e pgsql.query   -e pgsql.parameter_value


[root@vm141 ~]# sudo tshark -i ens33 -f "tcp port 5432"   -Y 'pgsql.query or pgsql.parameter_value'   -T fields   -e pgsql.query   -e pgsql.parameter_value
Running as user "root" and group "root". This could be dangerous.
Capturing on 'ens33'
	postgres,postgres,UTF8,ISO,Asia/Shanghai
SET extra_float_digits = 2	
SET application_name = 'PostgreSQL JDBC Driver'	
SELECT * FROM t_json WHERE id = $1 AND data = $2	


像这种绑定绑定变量的SQL场景,使用tshark抓包 也未能抓到参数数据。问题排查起来难度还是很大。
好在开发在生产和测试由于JDBC配置的参数不同,最终定位到了这个问题。

思考

   stringtype 为默认值specified时,
实体类 ID为string 时  程序会报错。
实体类 ID为long时  程序不会会报错。

stringtype=unspecified时
实体类 ID为string 时  程序不会报错。
实体类 ID为long时  程序不会报错。

为什么JDBC会有stringtype这个参数的设计,我是有点没太想明白。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐