You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MyBatis中ZonedDateTime插入MySQL Date列报错的解决方法咨询

解决ZonedDateTime插入MySQL Date列的MyBatis报错问题

你遇到的错误是因为MyBatis默认没正确处理ZonedDateTime到MySQL DATE列的转换,直接把对象序列化后传入,导致数据库无法解析二进制数据。下面是几种可行的解决方法,同时能保留Java代码里的ZonedDateTime字段,方便后续DBA修改列类型:

方案一:自定义MyBatis类型处理器(推荐)

这种方式不用修改实体类字段,既能适配MySQL的DATE列,又能保留ZonedDateTime的完整信息。

  1. 编写自定义类型处理器:
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;
import java.time.ZonedDateTime;

public class ZonedDateTimeToDateTypeHandler extends BaseTypeHandler<ZonedDateTime> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, ZonedDateTime parameter, JdbcType jdbcType) throws SQLException {
        // 提取ZonedDateTime的日期部分,转为java.sql.Date插入数据库
        ps.setDate(i, Date.valueOf(parameter.toLocalDate()));
    }

    @Override
    public ZonedDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 从数据库读取Date后,还原为带默认时区的ZonedDateTime
        Date date = rs.getDate(columnName);
        return date != null ? date.toLocalDate().atStartOfDay(java.time.ZoneId.systemDefault()) : null;
    }

    @Override
    public ZonedDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Date date = rs.getDate(columnIndex);
        return date != null ? date.toLocalDate().atStartOfDay(java.time.ZoneId.systemDefault()) : null;
    }

    @Override
    public ZonedDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Date date = cs.getDate(columnIndex);
        return date != null ? date.toLocalDate().atStartOfDay(java.time.ZoneId.systemDefault()) : null;
    }
}
  1. 在MyBatis全局配置文件中注册处理器:
<typeHandlers>
    <typeHandler handler="com.yourpackage.ZonedDateTimeToDateTypeHandler" 
                 javaType="java.time.ZonedDateTime" 
                 jdbcType="DATE"/>
</typeHandlers>
  1. (可选)在Mapper映射中指定处理器(全局注册后可省略):
<insert id="insertAuditRecord">
    INSERT INTO audit_table (auditDate, ...)
    VALUES (#{auditDate, typeHandler=com.yourpackage.ZonedDateTimeToDateTypeHandler}, ...)
</insert>

方案二:插入时临时提取LocalDate

如果不想写自定义处理器,可在Service层或Mapper参数中提取ZonedDateTime的日期部分,实体类仍保留ZonedDateTime字段:

Service层处理示例:

public void saveAudit(AuditRecord record) {
    // 提取日期部分用于插入
    LocalDate auditDate = record.getAuditDate().toLocalDate();
    auditMapper.insertAudit(auditDate, record.getOtherField());
}

对应的Mapper XML:

<insert id="insertAudit">
    INSERT INTO audit_table (auditDate, other_field)
    VALUES (#{auditDate, jdbcType=DATE}, #{otherField})
</insert>

方案三:转为java.util.Date(不推荐)

这种方式可行,但会丢失时区信息,后续恢复ZonedDateTime会有问题:

ZonedDateTime zonedDateTime = ...;
// 转换为java.util.Date,插入时MyBatis会自动转为java.sql.Date
Date utilDate = Date.from(zonedDateTime.toInstant());

无论选哪种方案,实体类都能保留ZonedDateTime字段,后续DBA修改列类型为DATETIME/TIMESTAMP时,只需调整转换逻辑或直接移除处理器,不用大面积改代码。

内容的提问来源于stack exchange,提问作者Some User

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:43:23