今天在线上查看数据的时候,发现两个datetime时间字段取duration的时候差了一秒。
mysql字段
mysql时间字段都是datetime类型,小数点都是0。两个字段分别是2024-04-19 23:58:16和2024-04-19 23:58:24,时间差是7,正确的时间差应该是8.
查看时间差代码
使用Duration的between静态方法,输入的两个时间都去除了小数点。
1 2
| Duration between = Duration.between(time1.withNano(0), time2.withNano(0)); long betweenSeconds = between.getSeconds();
|
查看时间保存代码
由于上述时间差的代码没有问题,那么只有可能时间字段保存有错误。想了一下,可能是由于小数点的问题,时间加了1秒,写了一个demo验证下猜想是否正确。
1 2 3 4 5 6 7 8 9 10 11 12
| @Test public void test() { WorkOrderDetailFixData workOrderDetailFixData = workOrderDetailFixDataMapper.selectById(225222968040494214L);
workOrderDetailFixData.setUpdateTime(LocalDateTime.now()); this.workOrderDetailFixDataMapper.updateById(workOrderDetailFixData);
log.info("time_diff = " + associateService.timeDiff(workOrderDetailFixData.getPreStationOutTime(), workOrderDetailFixData.getOutTime())); }
|
PS: java中时间小数点最多是9位,mysql是6位。
全局修改
知道问题后,首先check下该服务的数据库中所有的时间字段是什么类型,小数点几位,
查看了所有表,全部是datetime,并且小数点都是0。
这样的话,我们只需要修改LocalDateTime的typeHandler即可,找了一圈,没有发现可以一次性修改的的扩展方式。
于是只能把源代码复制到src中,修改setNonNullParameter方法,去掉小数点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package org.apache.ibatis.type;
import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDateTime; import java.util.Objects;
public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> { @Override public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException { if (Objects.nonNull(parameter)) { ps.setObject(i, parameter.withNano(0)); } else { ps.setObject(i, null); } }
@Override public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException { return rs.getObject(columnName, LocalDateTime.class); }
@Override public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return rs.getObject(columnIndex, LocalDateTime.class); }
@Override public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getObject(columnIndex, LocalDateTime.class); } }
|
复制的源代码如何生效
java的Classloader是不会加载重复的class文件,只会加载先找到的先加载,由于在src下,会优先于jar的加载
classpath确认
发布前,我们查看启动脚本中的classpath,由于我们使用的是jib的插件,启动的classpath在jib-classpath-file中。
1
| /app/resources:/app/classes:/app/libs/ams-service-adapter-0.1.181-SNAPSHOT.jar:......
|
我们看到classes在jar之前,这样确保typeHandler能够正确load我们重写的类。
结论
上线后,观察数据,bug修复。
这样的修复方法比较适合数据库时间字段全部是小数点是0的情况,否则就自定义一个typeHandler,手动引入该handler, 以mybatis plus为例。
1 2 3 4 5
| @TableName(autoResultMap = true) public class Process extends BasePo { @TableField(typeHandler = ListStringToJsonTypeHandler.class) private List<String> collectKey; }
|