You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring JDBC创建用户时触发PSQLException问题排查求助

排查PostgreSQL插入用户时的SQL类型推断错误

先来看你遇到的报错核心:

ERROR 16444 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO users (name, surname, year_of_birth, email, password, position_id) VALUES (:name, :surname, :year_of_birth, :email, :password, (SELECT id FROM positions WHERE name = :position))]; nested exception is org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of org.springframework.jdbc.core.namedparam.MapSqlParameterSource. Use setObject() with an explicit Types value to specify the type to use.] with root cause org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of org.springframework.jdbc.core.namedparam.MapSqlParameterSource. Use setObject() with an explicit Types value to specify the type to use.

这个报错的本质是PostgreSQL没法自动推断某个参数对应的SQL类型,结合你贴的代码,我找到了几个关键问题点:

问题1:year_of_birth参数类型不匹配

在Dao层的getParameters方法里,你把user.getYearOfBirth()(原本是Integer类型)转成了字符串存入参数Map:

parameters.put("year_of_birth", user.getYearOfBirth().toString());

但数据库里users表的year_of_birth字段是INTEGER类型,当你传入字符串格式的数字时,PostgreSQL无法自动完成类型转换,直接导致了类型推断失败。

问题2:(次要)参数类型未显式声明

虽然MapSqlParameterSource通常能自动识别基本类型,但如果参数和数据库字段类型不匹配,就容易触发这类推断错误。不过解决了第一个问题后,这个大概率也会跟着好起来。

解决方案

1. 修正year_of_birth的参数类型

getParameters方法里的year_of_birth参数改为直接传入Integer类型,不要转成字符串:

private Map<String, Object> getParameters(User user) {
    Map<String, Object> parameters = new HashMap<>();
    parameters.put("name", user.getName());
    parameters.put("surname", user.getSurname());
    // 直接传入Integer类型,去掉toString()转换
    parameters.put("year_of_birth", user.getYearOfBirth());
    parameters.put("email", user.getEmail());
    parameters.put("password", user.getPassword());
    parameters.put("position", user.getPosition().toString());
    return parameters;
}

2. (可选)显式指定所有参数的SQL类型

如果修改后还是有问题,可以换用MapSqlParameterSourceaddValue方法,显式指定每个参数对应的SQL类型,彻底避免推断错误:

SqlParameterSource source = new MapSqlParameterSource()
    .addValue("name", user.getName(), Types.VARCHAR)
    .addValue("surname", user.getSurname(), Types.VARCHAR)
    .addValue("year_of_birth", user.getYearOfBirth(), Types.INTEGER)
    .addValue("email", user.getEmail(), Types.VARCHAR)
    .addValue("password", user.getPassword(), Types.VARCHAR)
    .addValue("position", user.getPosition().toString(), Types.VARCHAR);

3. 顺便验证枚举匹配情况

另外可以检查下User.Position枚举的toString值和positions表中的name值是否完全一致(PostgreSQL是大小写敏感的),比如数据库里是'CEO',枚举toString后是"CEO",这个是没问题的,但如果有大小写不一致的情况,后续可能会出现position_id为null的问题,提前排查下更稳妥。

测试建议

修改完代码后,重新尝试创建用户,应该就能解决这个类型推断的问题了。如果还是报错,可以再检查下有没有参数是null值(比如year_of_birth是否允许为null,如果允许的话要确保参数处理逻辑正确)。

内容的提问来源于stack exchange,提问作者Dani221029

火山引擎 最新活动