记ShardingSphere-JDBC的一个坑(SQLFeatureNotSupportedException: getObject with type)

近日在使用Mybatis的时候出现了这么一个奇怪的报错:SQLFeatureNotSupportedException: getObject with type

现场还原:
Mybatis: 3.5.2+
JDBC: shardingsphere-jdbc 4.0.1-

Error attempting to get column 'update_time' from result set.  Cause: java.sql.SQLFeatureNotSupportedException: getObject with type
; getObject with type; nested exception is java.sql.SQLFeatureNotSupportedException: getObject with type
org.springframework.dao.InvalidDataAccessApiUsageException: Error attempting to get column 'update_time' from result set.  Cause: java.sql.SQLFeatureNotSupportedException: getObject with type
; getObject with type; nested exception is java.sql.SQLFeatureNotSupportedException: getObject with type
	at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:96)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:74)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
	at com.sun.proxy.$Proxy193.selectOne(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:159)
	...
	Caused by: java.sql.SQLFeatureNotSupportedException: getObject with type
	at org.apache.shardingsphere.shardingjdbc.jdbc.unsupported.AbstractUnsupportedOperationResultSet.getObject(AbstractUnsupportedOperationResultSet.java:223)
	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)

让我们来找一下原因,定位到错误提示的最后一句,发现问题出在Mybatis的LocalDateTimeTypeHandler这个类型转化器上,代码如下:

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

Mybatis的TypeHandelr试图调用ResultSet.getObject()获取结果,但是JDBC却说我们不支持这玩意儿?

我们去Mybatis那边看一下,发现了一些端倪。下方是Mybatis的更新报告

在这里插入图片描述

(LocalDateTimeTypeHandler要求JDBC支持4.2 API,是不是我们的JDBC版本低了)

JDBC对应的API(Since 1.8)
在这里插入图片描述

再去看一眼我们的JDBC(shardingsphere),好家伙,这玩意儿居然才JDK7,LocalDateTime是JDK8才出来的日期类型,现在问题找到了,我们的Mybatis版本太高了,或者说JDBC的版本太低了。

解决方案:

  1. 降低Mybatis版本至3.5.1及以下(不推荐)
  2. 升级JDBC版本(shardingsphere)(可是这已经是最新版本了,此路不通 )

近日,SharingSphere发布了4.1.0版本,终于升级到了JDK8,这个BUG也就不复存在了。

  1. 改写LocalDateTimeTypeHandler(既然JDBC不支持(LocalDateTime)rs.getObject(),那我们就不用好了,用自定义的转换器去替换Mybatis的转换器)。如下:
@Component
//定义转换器支持的JAVA类型
@MappedTypes(LocalDateTime.class)
//定义转换器支持的数据库类型
@MappedJdbcTypes(value = JdbcType.TIMESTAMP, includeNullJdbcType = true)
public class CustomLocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
   private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);

   @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 target = rs.getString(columnName);
       if (StringUtil.isEmpty(target)) {
           return null;
       }
       return LocalDateTime.parse(target, dateTimeFormatter);
   }
       @Override
   public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
       String target = rs.getString(columnIndex);
       if (StringUtil.isEmpty(target)) {
           return null;
       }
       return LocalDateTime.parse(target, dateTimeFormatter);
   }

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

以上代码并不完全,LocalDateTypeHandler也需要改写,但是高度雷同,就不贴出了。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值