MyBatis+Sharding-JDBC实体类LocalDateTime类型字段查询报SQLFeatureNotSupportedException: getObject with type

发布时间 2023-05-31 22:44:32作者: cdfive

问题

最近协助渠道组开发新需求,封装实现了一个公共模块供不同渠道项目使用。
以前各个渠道项目有很多相似的菜单和功能,各自项目里自己的代码实现,本公共模块对新需求的功能点进行抽象,减少重复代码,提高模块复用性和可维护性。
目前有2个渠道项目接入了该公共模块,自测时发现其中1个运行正常,另1个项目运行报错,日志如下:

Error attempting to get column ‘create_time’ from result set.
...
at com.xxx.xxx(XxxServiceImpl.java:76)
Caused by: java.sql.SQLFeatureNotSupportedException: getObject with type
	at org.apache.shardingsphere.shardingjdbc.jdbc.unsupported.AbstractUnsupportedOperationResultSet.getObject(AbstractUnsupportedOperationResultSet.java:221)
	at org.apache.ibatis.type.LocalDateTimeTypeHandler.getNullableResult(LocalDateTimeTypeHandler.java:38)
	at org.apache.ibatis.type.LocalDateTimeTypeHandler.getNullableResult(LocalDateTimeTypeHandler.java:28)
	at org.apache.ibatis.type.BaseTypeHandler.getResult(BaseTypeHandler.java:81)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyAutomaticMappings(DefaultResultSetHandler.java:521)

注意到日志里的shardingjdbc,该渠道项目使用了shardingjdbc进行分表。

在公共模块里使用了MyBatis,公共的实体类中定义了LocalDateTime类型字段:

@TableField(value = "create_time", insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER)
private LocalDateTime createTime;

@TableField(value = "update_time" insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER)
private LocalDateTime updateTime;

本地启动项目调试,在LocalDateTimeTypeHandler类的38行rs.getObject(columnName, LocalDateTime.class)打上断点:

public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
  ...
  @Override
  public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getObject(columnName, LocalDateTime.class);
  }
}

进到了AbstractUnsupportedOperationResultSet类,抛出了SQLFeatureNotSupportedException异常。

  • mybatis-plus-boot-starter版本:3.1.2
  • sharding-jdbc-core版本:4.1.1
    查询资料发现2者有冲突。

解决

通过自定义类扩展TypeHandler来处理LocalDateTime类型字段。

/**
 * @author cdfive
 */
@MappedTypes(LocalDateTime.class)
@MappedJdbcTypes(value = JdbcType.TIMESTAMP, includeNullJdbcType = true)
public class CustomLocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType)
            throws SQLException {
        if (parameter != null) {
            ps.setString(i, dateTimeFormatter.format(parameter));
        }
    }

    @Override
    public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String columnValue = rs.getString(columnName);
        if (StringUtils.isEmpty(columnValue)) {
            return null;
        }
        return LocalDateTime.parse(columnValue, dateTimeFormatter);
    }

    @Override
    public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String columnValue = rs.getString(columnIndex);
        if (StringUtils.isEmpty(columnValue)) {
            return null;
        }
        return LocalDateTime.parse(columnValue, dateTimeFormatter);
    }

    @Override
    public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String columnValue = cs.getString(columnIndex);
        if (StringUtils.isEmpty(columnValue)) {
            return null;
        }
        return LocalDateTime.parse(columnValue, dateTimeFormatter);
    }
}

通过DateTimeFormatter进行格式转换。

在项目的yml配置文件里指定类型处理类的包路径:

mybatis-plus:
  type-handlers-package: com.xxx.mybatis.typehandler

参考