0. 서론
앞선 글에서 자바의 날짜/시간과 관련된 클래스들에 대해서 알아보았습니다. 이번 글에선 JPA를 이용해서 자바의 클래스들이 MySQL에서 어떤 타입으로 변경 되는지를 알아보고, MySQL에 있는 데이터 타입에 대해서 정리하겠습니다.
💡목표
1. Java의 날짜/시간 클래스가 어떻게 변경되는지 확인한다.
2. DB에 있는 날짜/시간 데이터 타입에 대한 특징을 이해한다.
1. Java의 클래스는 어떻게 바뀌는가?
이번 시간엔 지금까지 알아본 Java의 다양한 날짜/시간 클래스들이 JPA를 통해 기본적으로 어떻게 변환이 되는지, 어떤 타입으로 변경이 가능한지에 대해서 알아보겠습니다.
아래 코드는 예제로 사용할 코드입니다. 앞선 포스팅에서 다룬 클래스인 `LocalDate`, `LocalTime`, `LocalDateTime`, `ZonedDateTime`, `Instant`가 어떤 식으로 변경되는지 확인해 보겠습니다.
@Getter
@NoArgsConstructor
@Entity
public class TimeHolder {
@Id
@GeneratedValue
private Long id;
private Long clock;
private Instant instant;
private LocalDate localDate;
private LocalTime localTime;
private LocalDateTime localDateTime;
private ZonedDateTime zonedDateTime;
}
`@Column`을 통해 타입을 따로 설정하지 않으면 아래와 같은 테이블이 생성됩니다.
Java | MySQL |
LocalDate | Date |
LocalTime | Time(6) |
LocalDateTime | DateTime(6) |
ZonedDateTime | DateTime(6) |
Instant | DateTime(6) |
MySQL의 `DateTime` 타입으로 설정된 값들은 `TimeStamp`로도 변경이 가능합니다.
@Getter
@NoArgsConstructor
@Entity
public class TimeHolder {
@Id
@GeneratedValue
private Long id;
private Long clock;
@Column(columnDefinition = "timestamp")
private Instant instant;
private LocalDate localDate;
private LocalTime localTime;
@Column(columnDefinition = "timestamp")
private LocalDateTime localDateTime;
@Column(columnDefinition = "timestamp")
private ZonedDateTime zonedDateTime;
}
아래 표는 StackOverFlow에서 작성된 Java 클래스가 JDBC를 통해서 SQL의 표준 데이터 타입으로 어떻게 변경되는지를 보여주는 표입니다. 앞서 살펴본 JPA를 통해서 변경되는 것과는 약간 차이가 있네요. 그리고 MySQL의 경우 SQL 표준 데이터 타입과 달리 TimeStamp가 따로 Timezone 저장하지 않고, MySQL에 저장된 timezone을 따릅니다.
더 자세한 내용은 이 링크를 통해서 확인할 수 있습니다.
2. MySQL의 날짜/시간 데이터 타입
JPA를 통해 확인했던 Java <-> MySQL 결과에서 확인한 MySQL의 데이터 타입들에 대해서 정리해 보겠습니다.
1. Date
The DATE type is used for values with a date part but no time part. MySQL retrieves and displays DATE values in 'YYYY-MM-DD' format. The supported range is '1000-01-01' to '9999-12-31'.
- MySQL The DATE, DATETIME, and TIMESTAMP Types
`Date` 타입은 1000-01-01부터 9999-12-31까지의 날짜를 표현합니다. 이때, 시간과 관련된 정보는 담지 않습니다. `Date`타입은 3바이트를 사용합니다. 관련된 내용을 테스트해 보았는데, 사용하고 있는 컴퓨터의 MySQL에 Strict Mode가 설정되지 않았다면, 연도에 있어서 1000보다 아래의 값을 입력해도 저장이 되지만, 9999보다 높은 값을 저장하는 것은 허용되지 않습니다.
2. Time
MySQL retrieves and displays TIME values in 'hh:mm:ss' format (or 'hhh:mm:ss' format for large hours values). TIME values may range from '-838:59:59' to '838:59:59'. The hours part may be so large because the TIME type can be used not only to represent a time of day (which must be less than 24 hours), but also elapsed time or a time interval between two events (which may be much greater than 24 hours, or even negative).
- MySQL The DATE, DATETIME, and TIMESTAMP Types
Docs에 따르면 `TIme`은 `hh:mm:ss` 혹은 `hhh:mm:ss`의 형태를 표현하고, '-838:59:59'부터 '838:59:59'까지 범위에 있는 값을 표현할 수 있습니다. 그리고 `Time`은 3바이트를 사용합니다.
Time의 경우 주어진 범위의 값을 모두 표현하는 것을 확인했고, 그 범위를 넘어가는 값은 저장할 수 없다는 것을 아래 사진을 통해서 확인할 수 있습니다. 범위를 넘어가는 값을 입력하면 `Error Code: 1292. Incorrect time value: '839:00:00' for column 'local_time' at row 1`라는 에러코드가 출력됩니다.
위의 사진을 보면 초보다 더 작은 값을 저장하고 있는 것을 확인할 수 있습니다. MySQL에서 시간을 저장하는 `Time`, `DateTime`, `TimeStamp`는 초보다 작은 단위의 값을 저장할 수 있기 때문입니다. 각 자릿수에 따른 추가 저장 용량이 필요하고 그에 대한 값은 아래 표를 통해서 확인할 수 있습니다. JPA를 통해서 Table을 생성하면 기본적으로 6자리를 사용하기 때문에, Time(6)이 저장되면 6바이트가 사용됩니다.
0 | 0 bytes |
1, 2 | 1 byte |
3, 4 | 2 bytes |
5, 6 | 3 bytes |
Fractional Seconds PrecisionStorage Required
출처 : https://dev.mysql.com/doc/refman/8.0/en/storage-requirements.html#data-types-storage-reqs-date-time
3. DateTime
The DATETIME type is used for values that contain both date and time parts. MySQL retrieves and displays DATETIME values in 'YYYY-MM-DD hh:mm:ss' format. The supported range is '1000-01-01 00:00:00' to '9999-12-31 23:59:59'.
- MySQL The DATE, DATETIME, and TIMESTAMP Types
`DateTime` 타입은 Date와 Time을 합친 것으로 보면 되겠습니다. Java에서 `LocalDateTime`이 `LocalDate`와 `LocalTime`이 합친 것처럼, `DateTime`도 동일합니다. 1000-01-01 00:00:00 ~ 9999:12:31 23:59:59까지 범위를 표현합니다.
`DateTime`은 8바이트를 사용하는 데이터타입입니다. 앞서 `Time`에서 확인한 것처럼 `DateTime`도 fractions seconds를 포함하고 있고, JPA가 datetime(6)으로 생성하기 때문에 총 11바이트를 사용한다고 생각하면 됩니다.
`DateTime`도 동일하기 `MySQL`이 Strict Mode가 설정되지 않았기 때문에, `Date`때와 마찬가지로 연도에 대한 범위에 대한 제한을 할 수 없었습니다. 하지만 시간 부분은 'Time`과 달리 하루에 사용하는 00:00:00 ~ 23:59:59까지의 값만을 표현할 수 있었습니다.
4. TimeStamp
The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC.
- MySQL The DATE, DATETIME, and TIMESTAMP Types
`TimeStamp`는 앞선 Java의 `Instant`와 비슷한 개념의 데이터 타입입니다. 1970-01-01 00:00:00부터 현재 시점까지 흐른 초를 기반으로 날짜를 표현합니다. `TimeStamp`는 4바이트이기 때문에, 2038-01-19 03:14:07까지만 표현이 가능합니다. 관련된 내용은 `2038년 문제`를 살펴보면 이해하기 쉬울 것입니다.
`TimeStamp`의 특징이라고 하면 MySQL의 TimeZone을 따른다는 점입니다. 이 부분이 앞서 살펴본 `DateTime`과 큰 차이점이라고 볼 수 있습니다.
`TimeStamp`에 저장된 값은, TimeZone을 변경하면 그에 따라서 변경이 됩니다.
'2023-12-23 09:30:43' 아래와 같은 값을 저장한 TimeStamp 데이터가 있다면, 해당 TimeStamp는 저의 MySQL에 저장된 TimeZone을 따를 것입니다. 저의 PC의 MySQL의 TimeZone은 'Asia/Seoul'로 설정되었기 때문에, 현재 시간은 UTC에 비해 9시간 빠를 것입니다.
`SET time_zone = '+0:00';`를 통해 TimeZone을 UTC로 맞춰준 후 TimeStamp에 저장된 값을 확인하면 본래 저장된 '2023-12-23 09:30:43'에서 9시간이 감소한 '2023-12-23 00:30:43'이 출력됩니다.
정리
지금까지 Java의 시간 클래스가 JPA를 통해서 어떤 클래스로 변경되는지 확인했고, MySQL에서의 날짜/시간과 관련된 데이터 타입에 대해서 알아보았습니다. 이 시리즈는 서로 다른 TimeZone에서도 똑바로 동작하게 하려면 어떤 시간 클래스와 데이터 타입을 사용해야 하는지를 정리하는 것이 목적이었습니다. 1편과 2편을 통해서 어떤 타입을 선택하는 것이 좋을지 대략 감이 잡히네요. 다음 편에서는 알 수 있다면, 다른 기업의 사례를 살펴보고, 글로벌 서비스가 동작하는 것을 보며 어떤 타입을 사용할지 고민해 보겠습니다.
참고 자료
https://dev.mysql.com/doc/refman/8.0/en/datetime.html
https://dev.mysql.com/doc/refman/8.0/en/time.html
https://dev.mysql.com/doc/refman/8.0/en/storage-requirements.html#data-types-storage-reqs-date-time
https://stackoverflow.com/questions/32437550/whats-the-difference-between-instant-and-localdatetime