PG JDBC一个奇怪的参数设计
·
简介
最近开发在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这个参数的设计,我是有点没太想明白。
更多推荐
所有评论(0)