40 | PreparedStatement 的 setObject 空值处理摘要PreparedStatement.setObject(i, null)用 MySQL 驱动会报错「Column ‘xx’ cannot be null」因为 JDBC 规范里 null 没有类型信息驱动不知道用什么 SQL 类型。一、问题现象StringsqlINSERT INTO user(name, age) VALUES(?, ?);PreparedStatementpsconn.prepareStatement(sql);ps.setObject(1,Alice);ps.setObject(2,null);// ❌ MySQL 驱动报错ps.executeUpdate();报错Column age cannot be null如果数据库字段不允许 NULL更隐蔽的问题即使字段允许 NULLsetObject(i, null)在某些驱动里也不生成正确的NULLSQL而是生成空字符串或报错。二、踩坑现场场景MyBatis 里传 null!-- ❌ null 值插入某些驱动下报错 --insertidinsertUserINSERT INTO user(name, age) VALUES(#{name}, #{age})/insert当age null时MyBatis 底层调用setObject(i, null)如果没指定jdbcTypeMySQL 驱动会报错。三、原理解析3.1 setObject 的 null 困境JDBC 规范PreparedStatement.setObject(int, Object)的第二个参数是null时驱动不知道这个参数的 SQL 类型是什么VARCHARINTEGERDATE。MySQL 驱动的处理5.1.x很多场景下直接报错或生成错误的 SQL8.0.x部分场景能推断但不保证3.2 正确做法setNull() 或指定 jdbcType// ✅ 方式一用 setNull()ps.setNull(2,Types.INTEGER);// 明确告诉驱动类型是 INTEGER// ✅ 方式二setObject 指定 SQL 类型ps.setObject(2,null,Types.INTEGER);// ✅ 方式三MyBatis 里指定 jdbcType四、正确写法4.1 普通 JDBC用 setNull() 或 setObject(…, Types.XXX)// ✅ 正确StringsqlINSERT INTO user(name, age) VALUES(?, ?);PreparedStatementpsconn.prepareStatement(sql);ps.setObject(1,Alice);if(agenull){ps.setNull(2,Types.INTEGER);// ✅ 明确类型}else{ps.setObject(2,age);}4.2 MyBatis指定 jdbcType!-- ✅ 正确指定 jdbcType --insertidinsertUserINSERT INTO user(name, age) VALUES(#{name}, #{age, jdbcTypeINTEGER})/insert或全局配置MyBatis 3.4.0!-- mybatis-config.xml --configurationsettingssettingnamejdbcTypeForNullvalueNULL//settings/configuration4.3 用 MyBatis 注解// ✅ 正确Param 指定 jdbcTypeInsert(INSERT INTO user(name, age) VALUES(#{name}, #{age}))voidinsertUser(Param(name)Stringname,Param(age)Integerage);// 需要在 mybatis-config.xml 里配置 jdbcTypeForNullNULL五、最佳实践✅ 5 条规范setObject(i, null)必须同时指定Types.XXXMyBatis 里遇到 null 值配置jdbcTypeForNullNULL数据库字段允许 NULL 时Java 实体用包装类型Integer而不是intMySQL 驱动升级到 8.0对 null 处理更友好写单测覆盖 null 值场景 各数据库驱动对 null 的处理差异数据库驱动版本setObject(i, null)行为MySQL5.1.x经常报错MySQL8.0.x能推断但建议显式指定PostgreSQL任意需要显式指定否则报错Oracle任意需要显式指定六、小结PreparedStatement.setObject(i, null)不指定类型时驱动不知道 SQL 类型可能报错正确做法用setNull(i, Types.XXX)或在setObject里指定第三个参数MyBatis 里配置jdbcTypeForNullNULL让 null 值正确映射为 SQLNULL数据库允许 NULL 的字段Java 实体用包装类型Integer/Long不要用基本类型下一篇预告MyBatis 的 #{} 和 ${} 用错就是 SQL 注入—— 动态表名、LIKE 查询的正确写法。更多资料一线大厂Java面试题合集