完整错误日志:
2019-09-20 08:35:37.860 信息 1 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat-1].[localhost].[/] : 初始化 Spring DispatcherServlet 'dispatcherServlet'
2019-09-20 08:47:29.726 错误 1 --- [nio-8081-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper:HOUR_OF_DAY:2 -> 3
2019-09-20 08:47:29.769 错误 1 --- [nio-8081-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] 在路径 [] 的上下文中抛出异常 [请求处理失败;嵌套异常是 org.springframework.orm.jpa.JpaSystemException:无法执行 询问;嵌套异常是 org.hibernate.exception.GenericJDBCException:无法执行查询] 有根本原因
java.lang.IllegalArgumentException:HOUR_OF_DAY:2 -> 3
在 java.base/java.util.GregorianCalendar.computeTime(来源未知) 〜[呐:呐]
在 java.base/java.util.Calendar.updateTime(来源未知)~[na:na]
在 java.base/java.util.Calendar.getTimeInMillis(来源未知) 〜[呐:呐]
这个问题已经在Java级别解决了,但是如何在mysql级别避免它。
事实上,查询甚至没有日期或时间。
@Query("select o from Order o where o.tickets is not null")
List<Order> ordersWithExistingTickets();
订单.java
@Entity
@Data
@Table(name="orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "PK")
private Long pk;
@Column(name = "createdTS")
private ZonedDateTime creationTime;
@Column(name = "tickets")
private String tickets;
public String getTickets() {
return tickets;
}
public void setTickets(String tickets) {
this.tickets = tickets;
}}
OrderRepository.java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("select o from Order o where o.tickets is not null")
List<Order> ordersWithExistingTickets();
}
原因是有一个日期时间字段(不是时间戳)在您的时区不可用。所以无法正确转换。
引起:java.lang.IllegalArgumentException:HOUR_OF_DAY:2 -> 3 在 java.base/java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2826) 在 java.base/java.util.Calendar.updateTime(Calendar.java:3428) 在 java.base/java.util.Calendar.getTimeInMillis(Calendar.java:1812) 在com.mysql.cj.result.SqlTimestampValueFactory.localCreateFromTimestamp(SqlTimestampValueFactory.java:108)
但通常您必须找到错误的记录并将其从数据库中删除或更新。
假设您正在使用 JOOQ 或有权执行此操作(因为使用 JPA 查询会导致问题)。这是我在 JUnit 集成测试中放入的代码片段
@Test
void ensureDatesAreOkayDuringDaylightSavings() {
var fallbackDayClauses = IntStream
.range(2000, LocalDate.now().getYear() + 1)
.mapToObj(year ->
LocalDate.ofYearDay(year, 1)
.withMonth(3)
.with(TemporalAdjusters.next(DayOfWeek.SUNDAY))
.with(TemporalAdjusters.next(DayOfWeek.SUNDAY))
.atTime(1, 59, 0)
)
.map(startDateTime ->
MYDB.CREATED_ON.between(
startDateTime,
startDateTime
.plusHours(1)
.plusMinutes(2)))
.collect(Collectors.toSet());
var potentialProblemIds = dsl.select(MYDB.ID)
.from(MYDB.TABLE)
.where(or(fallbackDayClauses))
.fetch(MYDB.ID);
assertThat(potentialProblemIds)
.isEmpty();
}
从 MySQL 5 迁移到 MySQL 8 连接器后,我们遇到了类似的问题。已验证以下链接
https://confluence.atlassian.com/jirakb/illegalargumentexception-hour_of_day-1063564762.html https://bugs.mysql.com/bug.php?id=96276 https://support.oracle.com/knowledge/Oracle%20Database%20Products/2863969_1.html
问题案例查询:当数据库和Java应用程序都在“美国/太平洋”时区时。
从“2019-03-10 02:00:00”和“2019-03-10 03:00:00”之间的点赞中选择actiondoneon;
+--------------------+
|行动完成 |
+--------------------+
| 2019-03-10 02:27:47 |
+--------------------+
一组 1 行(0.00 秒)
修复案例查询:
mysql> 选择convert_tz(actiondoneon, '美国/太平洋', '美国/太平洋') AS actiondoneon FROM likes WHERE actiondoneon BETWEEN '2019-03-10 02:00:00' AND '2019-03-10 03:00: 00';
+--------------------+
|行动完成 |
+--------------------+
| 2019-03-10 03:00:00 |
+--------------------+
一组 1 行(0.00 秒)
这里我们应用相同时区的convert_tz。 Convert_tz 函数四舍五入到最接近的正确时间戳。
示例:
mysql> SELECT invalid_timestamp、convert_tz(invalid_timestamp、'美国/太平洋'、'美国/太平洋') corrcted_timestamp FROM (SELECT '2019-03-10 02:27:47' invalid_timestamp) 选项卡;
+---------------------+-------------------------- --+
|无效时间戳 | corrcted_timestamp |
+---------------------+-------------------------- --+
| 2019-03-10 02:27:47 | 2019-03-10 03:00:00.000000 |
+---------------------+-------------------------- --+
一组 1 行(0.00 秒)
我在这里和其他地方尝试了建议。没有任何结果。 我必须将 MySQL 服务器时间设置为 UTC。按照此处提到的步骤进行操作 -> https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html 确保您注意“填充时区表”部分,这实际上是我们面临的问题。