相信99%的朋友都没有注意到的数据库时间类型的问题

文章目录

  • 创建表SQL
  • 实例小测试
  • 知识点小测试
  • 可以怎样处理
  • 只有查询有问题吗?
  • MySQL时间

很多时候,程序运行起来没有问题,并不代表程序就精确,例如创建时间多一秒少一秒这种事情,很多时候是没有人注意到这个问题。

当然,关于创建时间多一秒少一秒本身可能有不太重要,但是关于时间的查询,绝对就是一个比较严重的问题了。

有时候数据特殊性导致很难发现多一条少一条很难发现,但是少一条数据可能就是非常严重的问题。

本文,以MySQL和MyBatis为例,来说一下其中的关于时间的一些非常容易被忽略的小问题。

创建表SQL

create table datetime_timestamp (
  id int(20) unsigned not null auto_increment COMMENT '主键ID',
  datetime0 datetime COMMENT '没有小数部分,支持到秒',
  datetime3 datetime(3) COMMENT '3位小数,毫秒',
  datetime6 datetime(6) COMMENT '6位小数,微妙',
  timestamp0 TIMESTAMP COMMENT '没有小数部分,支持到秒',
  timestamp3 TIMESTAMP(3) COMMENT '3位小数,毫秒',
  timestamp6 TIMESTAMP(6) COMMENT '6位小数,微妙',
  primary key (`id`)
) engine = InnoDB auto_increment = 1 default CHARSET = utf8mb4 collate = utf8mb4_unicode_ci COMMENT = '时间';

实例小测试

// select * from datetime_timestamp where datetime0 = #{date}
// datetime0的数据类型是MySQL datetime
// DatetimeTimestampMapper datetimeTimestampMapper
List<DatetimeTimestampTO> listDatetime0ByDate(java.util.Date date);
@Resource
private DatetimeTimestampMapper datetimeTimestampMapper;

@Test
void listDatetime0ByDate() {
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.YEAR, 2023);
    calendar.set(Calendar.MONTH, 11);
    calendar.set(Calendar.DATE, 31);
    calendar.set(Calendar.HOUR_OF_DAY, 12);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    Date date = calendar.getTime();
    List<DatetimeTimestampTO> tos = datetimeTimestampMapper.listDatetime0ByDate(date);
    tos.forEach(System.out::println);
}

相信99%的朋友都没有注意到的数据库时间类型的问题_第1张图片

上面的数据查询能查询到id为5的那一条数据吗?

产生这种结果的原因是什么?

答案是:极小概率能查到

为什么呢?

知识点小测试

要知道上面问题的答案,得先搞清楚另一个问题:

java.util.Date类型和MySQL的datetime类型绝对匹配吗?

答案是不能,java.util.Date精度是毫秒,datetime默认是datetime(0),精度是秒。

select * from datetime_timestamp where datetime0 = #{date}

显然只有当java.util.Date的毫秒为0的时候才能匹配上。

我们可以看一下,MyBatis打印的SQL:

在这里插入图片描述

我们可以看到MyBatis将java.util.Date被转换为了java.sql.Timestamp

为什么会这样呢?

因为java.sql.PreparedStatement不支持java.util.Date,只支持java.sql.Date:
setDate

执行转换的是org.apache.ibatis.type.DateTypeHandler:

相信99%的朋友都没有注意到的数据库时间类型的问题_第2张图片

可以怎样处理

将毫秒部分设置为0,就可以
calendar.set(Calendar.MILLISECOND, 0);

@Resource
private DatetimeTimestampMapper datetimeTimestampMapper;

@Test
void listDatetime0ByDate() {
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.YEAR, 2023);
    calendar.set(Calendar.MONTH, 11);
    calendar.set(Calendar.DATE, 31);
    calendar.set(Calendar.HOUR_OF_DAY, 12);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    Date date = calendar.getTime();
    List<DatetimeTimestampTO> tos = datetimeTimestampMapper.listDatetime0ByDate(date);
    tos.forEach(System.out::println);
}

为啥我们查询基本很少发现时间查询有问题呢?

因为很多时候,时间更多的时候是查询的范围,要么就是查询到日期,不涉及到时间毫秒、微妙这种。

只有查询有问题吗?

不是,插入也有问题,插入的时候,如果大于等于500毫秒,会向前进1,小于500毫秒会被舍弃:
毫秒截断问题

有兴趣的朋友,可以自己动手尝试一下这个。

MySQL时间

MySQL的TIME、DATETIME、TIMESTAMP支持小数秒,最高精度能到微妙(6为小数)

  • TIME(fsp)
  • DATETIME(fsp)
  • TIMESTAMP(fsp)

FSP(fractional seconds precision)

CREATE TABLE time_table(time_v TIME(3), datetime_v DATETIME(6), timestamp_v TIMESTAMP(4));

对应TIME类型,java中可以使用java.sql.Time,很遗憾它只支持到毫秒,不能到微妙,如果需要到微妙需要自己实现,或者使用java.time.LocalTime,它支持到纳秒。

对于DATETIME、TIMESTAMP类型java中可以使用java.sql.Date,但是只能支持到毫秒,可以使用java.time.LocalDateTime或java.sql.Timestamp类型,可以支持到纳秒。

@Test
void listDatetime0ByTimestamp() {
    Timestamp timestamp = new Timestamp(System.currentTimeMillis());
    timestamp.setNanos(517024000);
    List<DatetimeTimestampTO> tos = datetimeTimestampMapper.listDatetime6ByTimestamp(timestamp);
    tos.forEach(System.out::println);
}

微妙

你可能感兴趣的:(数据库,MyBatis时间,Datetime类型,Timestamp精度,时间查询问题)