乐趣区

关于jpa:Spring-Data-JPA-报-HOUROFDAY-0-1异常的解决过程和方案

在进行数据查问时,控制台报了 Caused by: com.mysql.cj.exceptions.WrongArgumentException: HOUR_OF_DAY: 0 -> 1 异样,查问得悉:这是因为查 mysql 库,转换类型为 datetime 类型的字段引起的。

网上的解决方案有多种,大多数都是通过设置时区来解决的,但遗憾的是通过测试我发现即便在将数据跑在时区正确的数据库上,在执行起来我这依然出错。

最初发现我的问题呈现在夏令时上。

夏令时

记得小时候过过几个夏令时,大略的意思就是在某一天把表调快 1 个小时,而后再到某一天把表再调慢 1 个小时。这间接造成的问题的是:xxxx 年 xx 月 xx 日会对应上两个工夫戳。
比方咱们假如把表调慢的那一天是 2021 年 10 月 4 日的 12 点。具体的操作是过后钟第一次通过 2021 年 10 月 4 日 12 点时,咱们把表调到 2021 年 10 月 4 日 11 点。所以在 2021 年 10 月 4 日 11 点至 12 点,咱们会从新过一次。

对于工夫这块,已经回达到一个工夫戳为负的问题,也有那么点意思:https://segmentfault.com/q/1010000038248983,赶趣味的能够看看。

那么问题来了,比咱们记录用户的出世工夫,准确到分钟。如果这个人录入的是 2021 年 10 月 4 日 11 点 20 分,那咱们的零碎没有方法来精确的判断这个工夫是第一个 11 点 20 分,还是过 1 小时后的第二个 11 点 20 分。

夏令时,还给咱们带来的另一个问题。有些工夫是对应不上工夫戳的。
再比方咱们设置在 2021 年 5 月 1 日 0 时,将表调快 1 时,则在历史上不会呈现 2021 年 5 月 1 日 0 时至 1 时的工夫,所以如果咱们统计出世工夫点,用户写的是:2021 年 5 月 1 日 0 时 30 分,则该数据必须是个假数据。

排查

夏令时讲完后,咱们讲下排查过程。其实并不是所有的数据在查问时,都会报这种异样,所以要把那个非凡的点找进去,这里给一种最笨的展现办法:

    boolean last = false;
    int page = 0;
    Pageable pageable = PageRequest.of(page, 1);
    while (!last) {
      try {Page<Resident> residents = this.residentRepository.findAll(specification, pageable);
        page++;
        pageable = PageRequest.of(page, 1);
        last = residents.isLast();} catch (Exception e) {
        last = true;
        e.printStackTrace();
        this.logger.info("当前页" + pageable.getPageNumber());
      }
    }

最终控制台打印信息:2021-11-04 13:25:38.562 INFO 4226 --- [nio-8081-exec-7] c.y.s.service.ResidentServiceImpl : 当前页 1089

而后咱们去数据表中把这条记录查出来:

select * FROM resident limit 1089, 1

咱们发现此人的出生日期是 1947 年 4 月 15 日 0 时 0 分 0 分。其实这个日期用户仅仅是输出了 1947-4-15,只是咱们存的时候主动增加了 0 时 0 分 0 秒。但凑巧,这个数字对应的工夫戳,它恰好就是一个有效数字。

测试代码如下:

  @Test
  void time() {Calendar calendar = Calendar.getInstance();
    // 启用严格查看模式
    calendar.setLenient(false);
    calendar.set(1947, 3, 15, 0, 0, 0);
    System.out.println(calendar.getTime());
  }

异样内容:java.lang.IllegalArgumentException: HOUR_OF_DAY: 0 -> 1
它是在说:你说本人是 0 点出世的,然而本 JAVA 大牛查了一下,1947 年 4 月 15 日就没有 0 点,当天的最小值是 1 点。

解决问题

问题找到了,解决便是最简略的一环。

  1. 找到报错的历史数据,将 0 点改成 8 点。
  2. 找到历史的代码,将 0 点改成 8 点。
  public static Timestamp getTimeStampFormIdNumber(String idNumber) {
    // 进行出生日期赋值
    int year = Integer.valueOf(idNumber.substring(6, 10));
    int month = Integer.valueOf(idNumber.substring(10, 12));
    int day = Integer.valueOf(idNumber.substring(12, 14));

    Calendar calendar = Calendar.getInstance();
-   calendar.set(year, month - 1, day, 0, 0, 0);
+   calendar.set(year, month - 1, day, 12, 0, 0);
    return new Timestamp(calendar.getTimeInMillis());
  }

至于为什么改成 12 点,是因为我发现夏令时的批改(调快或调慢)都躲避了 12 点,所以工夫为 12 点,则能够无效防止有效工夫戳的问题. 当然了,你还能够改成 13 点,只有不是凌晨的那几个小时,都没有什么问题。

退出移动版