如何解决MyBatis映射MSSQL Time到Java LocalTime报错问题
该报错和MyBatis本身映射逻辑无关,核心原因是项目使用的jTDS JDBC驱动早已停止维护,最新正式版本仅支持JDBC 4.0规范,没有实现JDBC 4.2新增的ResultSet#getObject(int columnIndex, Class<T> type)重载方法。MyBatis内置的LocalTimeTypeHandler会直接调用该方法尝试获取LocalTime类型的返回值,jTDS未实现该方法就会抛出AbstractMethodError。
方案1(优先推荐):替换为微软官方MSSQL驱动
jTDS驱动最后一个正式版本发布于2013年,后续无任何功能更新,不仅不支持Java 8+的时间类型,还存在高版本SQL Server兼容性差、未修复安全漏洞等问题。直接替换为微软官方持续维护的mssql-jdbc驱动即可一劳永逸解决所有JDBC新特性兼容问题,替换后无需修改任何现有MyBatis映射配置,数据库TIME类型到POJOLocalTime属性的映射可以直接正常工作。
方案2(存量场景兼容):自定义jTDS适配的LocalTime类型处理器
如果受历史项目约束暂时无法替换jTDS驱动,可以自定义类型处理器,绕开jTDS未实现的接口方法,通过老版本JDBC接口获取java.sql.Time类型值后手动转换为LocalTime:
- 编写自定义类型处理器类,代码如下:
import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.time.LocalTime; @MappedTypes(LocalTime.class) @MappedJdbcTypes(JdbcType.TIME) public class JtdsLocalTimeTypeHandler extends BaseTypeHandler<LocalTime> { @Override public void setNonNullParameter(PreparedStatement ps, int i, LocalTime parameter, JdbcType jdbcType) throws SQLException { ps.setTime(i, Time.valueOf(parameter)); } @Override public LocalTime getNullableResult(ResultSet rs, String columnName) throws SQLException { Time sqlTime = rs.getTime(columnName); return sqlTime == null ? null : sqlTime.toLocalTime(); } @Override public LocalTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException { Time sqlTime = rs.getTime(columnIndex); return sqlTime == null ? null : sqlTime.toLocalTime(); } @Override public LocalTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { Time sqlTime = cs.getTime(columnIndex); return sqlTime == null ? null : sqlTime.toLocalTime(); } }
- 注册该类型处理器:
- 如果使用MyBatis Spring Boot Starter,直接将该类放到Spring Boot启动类的子包路径下,即可被自动扫描注册
- 如果使用原生MyBatis,可在
mybatis-config.xml中添加如下配置注册:
<typeHandlers> <typeHandler handler="替换为该类的全限定类名.JtdsLocalTimeTypeHandler"/> </typeHandlers>
注册完成后原有字段映射配置无需修改,即可正常完成TIME类型到LocalTime的转换。
除LocalTime外,jTDS驱动对所有Java 8新增的java.time包下的时间类型(LocalDate、LocalDateTime、OffsetDateTime等)都存在相同的兼容问题,只要使用MyBatis内置的对应类型处理器都会抛出相同的AbstractMethodError,如果坚持使用jTDS,需要为所有这些时间类型逐一编写兼容的自定义类型处理器。
内容的提问来源于stack exchange,提问作者Stotra

