LocalDateTime.MIN 은 Mysql의 DATETIME에 저장할 수 없다
안녕하세요. 졸린개발자입니다.
바로 들어가보시죠
LocalDateTime.MIN의 값은 무엇일까요?
java 21기준으로는
-999999999-01-01T00:00:00 이라고 하는군요.
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/LocalDateTime.html#MIN
Mysql DATETIME의 최소값은 몇일까요?
8.0 기준으로는
1000-01-01 00:00:00 이라고 합니다
https://dev.mysql.com/doc/refman/8.0/en/datetime.html
LocalDateTime.MIN의 값을 Mysql에 저장하려고 하면 어떻게 될까요?
간단하게 코딩을 해서 알아봅시다
public class User {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
private LocalDateTime createdAt;
}
위와 같은 Entity에 LocalDateTime.MIN을 저장해보려고 시도해봅시다.
Caused by: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Incorrect datetime value: '169087565-03-15 04:51:43' for column 'created_at' at row 1
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:96) ~[mysql-connector-j-9.4.0.jar:9.4.0]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:988) ~[mysql-connector-j-9.4.0.jar:9.4.0]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1166) ~[mysql-connector-j-9.4.0.jar:9.4.0]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1101) ~[mysql-connector-j-9.4.0.jar:9.4.0]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1448) ~[mysql-connector-j-9.4.0.jar:9.4.0]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1084) ~[mysql-connector-j-9.4.0.jar:9.4.0]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61) ~[HikariCP-6.3.3.jar:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java) ~[HikariCP-6.3.3.jar:na]
오류가 발생하는군요.
java에서 -999999999-01-01 00:00:00를 저장하고 싶지만
Mysql에서는 1000-01-01 00:00:00까지만 받을 수 있습니다
재밌는 점은, LocalDateTime의 MIN은 -999999999년인데
실제 저장하려고 시도한 값은 169087565년 이네요
아마 오버플로우 문제 같군요.
해결방법
결국 최소 기간을 저장할때
LocalDateTime.MIN 대신, 1000년 1월 1일 이후의 적당한 값으로 저장을 해야합니다.
방법은 여러가지가 있을 수 있을꺼 같군요
1. 필드를 세팅할때부터 1000년 1월 1일로 저장
2. Hibernate의 라이프사이클 hook 메서드를 통해, MIN을 저장하려고 시도하는것을 낚아채어, 1000년 1월 1일로 치환 (다시 가져올때에도 LocalDateTime.MIN으로 치환)
3. Hibernate의 Converter 적용을 통해, MIN을 저장하려고 시도하는것을 낚아채어, 1000년 1월 1일로 치환 (다시 가져올때에도 LocalDateTime.MIN으로 치환)
3. 등등.....
2번이나 3번이 좋아보이는군요
1번을 선택하면,
필드를 채워줄때마다 LocalDateTime.MIN 대신,
1000년 1월 1일을 넣어야 한다는 사실을 개발자는 항상 인지하고 있어야 합니다.
(유틸 함수로 작성할 수는 있겠지만, 여전히 그 함수를 호출해야하죠)
하지만 개발자는 가장 작은 값을 넣고 싶을때
거의 무의식적으로 LocalDateTime.MIN을 넣게 되니
이 사실을 모르는 개발자가 개발을 하게 되면, 버그가 일어날 여지가 있습니다.
저장할 때, Mysql에서의 가장 작은 값을 저장하는 것으로 알아서 바꿔주는것이
가장 개발자의 의도에 맞으면서도, Mysql에 대해 추가적으로 생각할 거리가 없게 만들게 되는거 같습니다.
정리
오늘도 재밌으셨나요?
섬네일 사진: Unsplash 의 Emma Swoboda