Spring JDBC创建用户时触发PSQLException问题排查求助
先来看你遇到的报错核心:
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类型
如果修改后还是有问题,可以换用MapSqlParameterSource的addValue方法,显式指定每个参数对应的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




