Spring项目登录查询抛出MyRowMapper不可序列化异常如何解决
报错根因
- 从异常堆栈最底层溯源,抛出
NotSerializableException的对象是com.example.dao.UserDao$MyRowMapper,也就是自定义的行映射类,这个类本身既不该作为SQL查询入参传给数据库,也没有实现序列化接口。 - 触发错误的直接原因是
UserDao.login方法在调用JdbcTemplate查询时参数顺序完全错误:- 调用
queryForList方法时,你将MyRowMapper实例放到了方法的可变参数位置,而queryForList(String sql, Object... args)重载会把第二个参数开始的所有入参,全部当作SQL占位符?的绑定值处理 - 你的SQL只有2个占位符,但因为多传了
MyRowMapper,实际会传入3个绑定参数;MySQL驱动收到非基础类型的MyRowMapper参数时,默认会尝试将对象序列化后传入数据库,而MyRowMapper未实现Serializable接口,因此直接抛出异常
- 调用
- 对应错误代码大概率是类似下面的写法:
// 错误示例:MyRowMapper被当成SQL绑定参数传入 jdbcTemplate.queryForList( "select * from user where email=? and pass=?", new MyRowMapper(), // 错误:这个位置应该传查询入参[email, password] email, password );
可落地修复方案
- 严格匹配
JdbcTemplate方法的签名传参,修正参数顺序:- 如果你不需要自定义RowMapper、只需要返回
List<Map<String, Object>>格式的查询结果,直接用queryForList传绑定参数即可:// 正确写法:SQL后直接跟和占位符一一对应的入参 List<Map<String, Object>> userList = jdbcTemplate.queryForList( "select * from user where email=? and pass=?", email, password ); - 如果你需要用
MyRowMapper做行映射,不要用queryForList,改用query方法并按正确顺序传参:// 正确写法:SQL → 绑定参数数组 → RowMapper List<User> userList = jdbcTemplate.query( "select * from user where email=? and pass=?", new Object[]{email, password}, new MyRowMapper() ); // 或者使用可变参数重载版本(注意顺序:SQL → RowMapper → 绑定参数) List<User> userList = jdbcTemplate.query( "select * from user where email=? and pass=?", new MyRowMapper(), email, password );
- 如果你不需要自定义RowMapper、只需要返回
- 防御性优化:如果
MyRowMapper是定义在UserDao内部的非静态内部类,建议改为static修饰的静态内部类,避免持有外部类引用带来的不必要开销;如果后续有序列化需求,再让其实现Serializable接口并指定序列化ID。 - 快速排查技巧:后续遇到类似SQL绑定参数错误的异常时,可在
JdbcTemplate执行前打断点,查看传入给ArgumentPreparedStatementSetter的参数列表,即可直接定位哪个非预期对象被误传为SQL入参。
内容的提问来源于stack exchange,提问作者user15550925
相关产品推荐
相关产品推荐

