如何在MyBatis查询时将Oracle Timestamp转为Java String(替代TO_CHAR)
当然可以!你已经在插入数据时用自定义TypeHandler搞定了类型转换,查询场景下完全可以用同样的思路实现,彻底摆脱对数据库TO_CHAR函数的依赖。下面给你详细讲两种可行的方案:
1. 先确保自定义TypeHandler支持双向转换
首先检查你现有的StringDateTypeHandler,它需要同时支持Java String → JDBC Timestamp(插入逻辑)和JDBC Timestamp → Java String(查询逻辑)。如果之前只实现了插入的setNonNullParameter方法,那得补全查询用的getResult系列方法。
举个完整的实现示例(适配MyBatis 3.2.6):
import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.*; import java.text.SimpleDateFormat; public class StringDateTypeHandler extends BaseTypeHandler<String> { // 用ThreadLocal解决SimpleDateFormat的线程安全问题 private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS")); // 插入时:String转Timestamp @Override public void setNonNullParameter(PreparedStatement ps, int i, String param, JdbcType jdbcType) throws SQLException { try { Timestamp timestamp = Timestamp.valueOf(param); ps.setTimestamp(i, timestamp); } catch (IllegalArgumentException e) { throw new SQLException("Invalid date string format: " + param, e); } } // 查询时:从ResultSet按列名取Timestamp转String @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { Timestamp ts = rs.getTimestamp(columnName); return ts != null ? DATE_FORMATTER.get().format(ts) : null; } // 查询时:从ResultSet按索引取Timestamp转String @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { Timestamp ts = rs.getTimestamp(columnIndex); return ts != null ? DATE_FORMATTER.get().format(ts) : null; } // 存储过程调用时取结果的转换 @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { Timestamp ts = cs.getTimestamp(columnIndex); return ts != null ? DATE_FORMATTER.get().format(ts) : null; } }
注意:Oracle的FF对应小数秒,这里用SSSSSS匹配6位精度,如果你的数据库精度更高,可以调整格式字符串。
2. 在查询时配置TypeHandler
有两种配置方式,按需选择:
方式一:在ResultMap中指定(推荐,灵活可控)
针对需要转换的字段,在ResultMap里显式指定typeHandler,这样只会影响该字段:
<resultMap id="TicketResultMap" type="com.yourpackage.Ticket"> <id column="SESSION_ID" property="sessionId"/> <result column="MEMBER_ID" property="memberId"/> <result column="KO_MEMBER_ID" property="koMemberId"/> <result column="KO_MEMBER_NAME" property="koMemberName"/> <result column="REASON" property="reason"/> <!-- 关键:给TICKET_DTM指定自定义TypeHandler --> <result column="TICKET_DTM" property="ticketDtm" typeHandler="com.yourpackage.StringDateTypeHandler"/> <result column="DATA_TYPE" property="dataType"/> </resultMap>
然后修改查询SQL,去掉TO_CHAR,直接查询原字段:
<select id="getTicketById" resultMap="TicketResultMap"> SELECT SESSION_ID, ct.MEMBER_ID as MEMBER_ID, ct.KO_MEMBER_ID as KO_MEMBER_ID, ct.KO_MEMBER_NAME as KO_MEMBER_NAME, ct.REASON as REASON, ct.TICKET_DTM as TICKET_DTM, ct.DATA_TYPE as DATA_TYPE FROM TICKET ct WHERE id=#{Id} fetch first 300 rows only </select>
方式二:全局注册TypeHandler(批量场景适用)
如果你的项目中有大量Timestamp转String的需求,可以在MyBatis配置文件中全局注册该Handler,让MyBatis自动匹配类型转换:
<configuration> <typeHandlers> <!-- 当Java类型是String,JDBC类型是TIMESTAMP时,使用自定义Handler --> <typeHandler handler="com.yourpackage.StringDateTypeHandler" javaType="java.lang.String" jdbcType="TIMESTAMP"/> </typeHandlers> <!-- 其他配置:environments、mappers等 --> </configuration>
这种方式不需要修改ResultMap,MyBatis会自动处理所有符合条件的字段,但要注意不要影响其他不需要转换的字段。
最后补充个小提醒
MyBatis 3.2.6是比较老的版本,要确保你的TypeHandler继承的BaseTypeHandler方法签名和版本匹配,上面的示例是完全适配这个版本的。另外,日期格式要和数据库存储的Timestamp精度保持一致,避免出现截断或格式不匹配的问题。
内容的提问来源于stack exchange,提问作者Biswajit




