说来惭愧,我做了10年程序员,对开源一点贡献都没有。想过提个PR修复一下,但是最近遇到了一个问题,觉得挺简单的,就直接提个PR。问题很简单,就是在使用mybatis和ShardingSphere的时候,有人在model类中使用了OffsetDateTime时间类型,发现会报错。Causedby:java.lang.ClassCastException:classjava.sql.Timestampcannotbecasttoclassjava.time.OffsetDateTime(java.sql.Timestampisinmodulejava.sqlofloader'platform';java.time.OffsetDateTime是在org.apache.ibatis.type.OffsetDateTimeTypeHandler.getNullableResult(OffsetDateTimeTypeHandler.java:28)处的org.apache.ibatis.type.OffsetDateTimeTypeHandler.java:28)处的org.apache.ibatis.type.OffsetDateTimeTypeHandler.getNullableResult(OffsetDateTimeTypeHandler.java:38)模块java.base.apache.ibatis.type.BaseTypeHandler.getResult(BaseTypeHandler.java:85)...99more这是一个简单的类型转换异常,于是查看了源码,首先看到了BaseTypeHandler#getResult方法,其实是根据列名返回查询结果。根据调用关系找到OffsetDateTimeTypeHandler的实现类。发现最终会调用rs.getObject()方法,所以实际上这个方法最终会走到ShardingSphere实现的getObject方法中去。看到这里,我已经明白为什么会报错了。Shardingsphere只判断了几个LocalDateTime等类型,并没有对这种特殊的时间类型进行处理。最终会转化为Timestamp,然后强制报错。最后调用ResultSetUtil#convertTimestampValue方法,可以看到确实如此。那么如果修改源码的话,其实很简单,getObject的判断加一个,convertTimestampValue加一个,就这么简单。@OverridepublicTgetObject(finalintcolumnIndex,finalClasstype)throwsSQLException{if(BigInteger.class.equals(type)){return(T)BigInteger.valueOf(getLong(columnIndex));}}elseif(Blob.class.equals(type)){return(T)getBlob(columnIndex);}elseif(Clob.class.equals(type)){return(T)getClob(columnIndex);}elseif(LocalDateTime.class.equals(type)||LocalDate.class.equals(type)||LocalTime.class.equals(type)||OffsetDateTime.class.equals(type)){return(T)ResultSetUtil。convertValue(mergeResultSet.getValue(columnIndex,Timestamp.class),类型);}else{return(T)ResultSetUtil.convertValue(mergeResultSet.getValue(columnIndex,type),type);}}privatestaticObjectconvertTimestampValue(finalObjectvalue,finalClass>convertType){Timestamptimestamp=(Timestamp)value;如果(罗calDateTime.class.equals(convertType)){returntimestamp.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();}if(LocalDate.class.equals(convertType)){returntimestamp.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();}if(LocalTime.class.equals(convertType)){returntimestamp.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();}if(OffsetDateTime.class.equals(convertType)){returntimestamp.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime();}returnvalue;}Fix一开始不想改源码,想着其他的实现方案,搜索后发现引入一个包就可以解决,就是mybatis的JSR310规范org.mybatismybatis-typehandlers-jsr3101.0.1他为什么能解决这个问题?我看了一下他包里的代码,不就是加了个TypeHandler自己处理吗。我们再来看一下OffsetDateTimeTypeHandler的实现。其实是自己解决的。直接返回OffsetDateTime,完全不会进入ShardingSphere的逻辑。这也是他能解决这个问题的原因。当然,如果你不想麻烦导入一个包,你也可以自己取出来指定。这个很简单,我就不多说了。提一个PR于是我想,这件事这么简单,我还不如给官方提一个PR,这里就教大家如何提PR。因为不是我们的项目,不能push代码,所以进入项目,然后fork,fork完成后,直接clone项目,然后执行命令。gitremoteaddupstreamhttps://github.com/apache/shardingsphere.git通过命令可以看到成功,所以就OK了,然后拉分支正常写代码。写好之后去我们的项目界面正常提交PR,然后就可以了。麻烦当然,过程并不是那么顺利,虽然只是很简单的修改。首先,这个验证给了我一个错误信息。第一点告诉我不要用*来引用。这其实就是IDEA的锅。如果同一个包中的类过多,它会自动将我们转换成星号。我们可以在Editor-CodeStyle-Java中的Imports下找到这两个选项,把它们都改成99即可,防止他自动把我们改成星号。还有一些其他的,比如后面没有空格,我忘记格式化了!然后老大回复看不下去,这段代码太恶心了,问能不能用java.time.temporal.TemporalAccessor来判断,不然时间类型那么多,乱七八糟。然后我翻译了一段英文,不知道老大看不懂,我跟他说,这个不容易,看这个接口,一大堆乱七八糟的类实现了他,其实我觉得我们涵盖了常用的一些它们中的都很好,其他特殊时间类型让它们用TypeHandler来处理。老大说,嗯,当然,如果没有办法判断这个接口,那我们也没什么办法,我说那不行。其实还有很多时间类型他会报错。最好的办法是把这个抽象出来,单独使用Mybatis作为一个实现类,但是这样会很费工夫。我太懒了,就是这样。...