You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动