MyBatis中ZonedDateTime插入MySQL Date列报错的解决方法咨询
解决ZonedDateTime插入MySQL Date列的MyBatis报错问题
你遇到的错误是因为MyBatis默认没正确处理ZonedDateTime到MySQL DATE列的转换,直接把对象序列化后传入,导致数据库无法解析二进制数据。下面是几种可行的解决方法,同时能保留Java代码里的ZonedDateTime字段,方便后续DBA修改列类型:
方案一:自定义MyBatis类型处理器(推荐)
这种方式不用修改实体类字段,既能适配MySQL的DATE列,又能保留ZonedDateTime的完整信息。
- 编写自定义类型处理器:
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; } }
- 在MyBatis全局配置文件中注册处理器:
<typeHandlers> <typeHandler handler="com.yourpackage.ZonedDateTimeToDateTypeHandler" javaType="java.time.ZonedDateTime" jdbcType="DATE"/> </typeHandlers>
- (可选)在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
相关产品推荐
相关产品推荐

