Hiện trạng theo thời gian

Kể từ thời điểm JDBC được phát minh và các giao diện của nó được tiêu chuẩn hóa, đã 20 năm trôi qua và trong thời gian này, rất nhiều thứ đã thay đổi.

Thứ nhất, thế giới đã trở nên toàn cầu và giờ đây một máy chủ có thể phục vụ người dùng từ khắp nơi trên thế giới. Tốc độ internet đã tăng lên. Do đó, một kiểu dữ liệu khác đã được thêm vào SQL để hoạt động theo thời gian. Bây giờ các loại trông như thế này:

  • DATE - lưu trữ ngày: năm, tháng, ngày.
  • TIME - lưu trữ thời gian: giờ, phút, giây.
  • DẤU THỜI GIAN - lưu trữ một thời điểm cụ thể: ngày, giờ và mili giây.
  • DẤU THỜI GIAN VỚI Múi giờ - DẤU THỜI GIAN và múi giờ (tên hoặc phần bù của múi giờ).

Thứ hai, Java đã giới thiệu API DateTime để quản lý thời gian toàn cầu. Nó có các lớp sau:

  • Ngày và giờ :
    • Ngày địa phương
    • Giờ địa phương
  • Thời điểm chính xác :
    • java.time.Instant
    • java.time.LocalDateTime
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime
  • Thời gian với múi giờ :
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime

Điểm thú vị thứ ba là nhiều máy khách SQL muốn nhận thời gian từ máy chủ đã có trong vùng cục bộ của họ . Tất nhiên, bạn có thể chuyển đổi thời gian nhanh chóng, nhưng nó không thuận tiện và có những sai lầm.

Ví dụ: tôi muốn lấy tất cả các tác vụ cho ngày hôm nay từ cơ sở dữ liệu. SQL Server có chức năng CURDATE() cho việc này. Chỉ có ở đây máy chủ ở Hoa Kỳ và tôi ở Nhật Bản. Và tôi muốn anh ấy trả lại tất cả các bản ghi cho “hôm nay của tôi”, chứ không phải “hôm nay của anh ấy”.

Nói chung, máy chủ SQL cũng phải có khả năng hoạt động thông minh với các máy khách ở các múi giờ khác nhau.

Các vấn đề hiện đại đòi hỏi các giải pháp hiện đại

Về nguyên tắc, các loại mới từ Java DateTime API và các loại từ SQL có thể được ánh xạ một cách thuận tiện. Để thể hiện kiểu DATE trong Java, bạn cần sử dụng lớp java.time.LocalDate từ API DateTime của JDK 8.

Loại TIME từ cơ sở dữ liệu có thể được biểu thị bằng hai loại từ Java: java.time.LocalTimejava.time.OffsetTime . Cũng không có gì phức tạp.

Một thời điểm cụ thể, được biểu thị bằng loại DẤU THỜI GIAN trong cơ sở dữ liệu, có thể được biểu thị bằng 4 loại trong Java:

  • java.time.Instant
  • java.time.LocalDateTime
  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

Và cuối cùng, DẤU THỜI GIAN VỚI Múi giờ có thể được biểu thị bằng hai loại:

  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

Vì bạn đã quen thuộc với DateTime API, nên việc ghi nhớ vấn đề này sẽ không khó đối với bạn :)

Tôi sẽ viết nó dưới dạng một bảng, như vậy sẽ dễ dàng hơn:

LOẠI SQL Loại Java
NGÀY java.time.LocalDate
THỜI GIAN java.time.LocalTime
java.time.OffsetTime
DẤU THỜI GIAN java.time.Instant
java.time.LocalDateTime
java.time.OffsetDateTime
java.time.ZonedDateTime
DẤU THỜI GIAN VỚI Múi giờ java.time.OffsetDateTime
_

Bắt ngày

Tôi có một tin tốt cho bạn. Lần đầu tiên trong một thời gian dài. Chúng ta có thể vượt qua giới hạn của phương thức getDate() , phương thức này trả về một kiểu Ngày java.sql.

Vấn đề là đối tượngtập hợp kết quảcó một phương thức thú vị khác - getObject() . Phương thức này nhận hai tham số: một cột và một loại, đồng thời trả về giá trị của cột được chuyển đổi thành loại đã cho. Dạng tổng quát của phương pháp như sau:

ClassName Name = getObject(column, ClassName);

Và nếu bạn muốn chuyển đổi loại DATE thành loại java.time.LocalDate , thì bạn cần phải viết một cái gì đó như:

LocalDate localDate = results.getObject(4, LocalDate.class);

Và bất kỳ DẤU THỜI GIAN nào nói chung đều có thể được chuyển đổi thành nhiều loại:

java.time.Instant instant = results.getObject(9, java.time.Instant.class);
java.time.LocalDateTime local = results.getObject(9, java.time. LocalDateTime.class);
java.time.OffsetDateTime offset = results.getObject(9, java.time. OffsetDateTime.class);
java.time.ZonedDateTime zoned = results.getObject(9, java.time. ZonedDateTime.class);

Quan trọng! Mã này sẽ không hoạt động nếu bạn có Trình điều khiển JDBC MySQL đã lỗi thời . Hãy chú ý đến phiên bản "mysql-connector-java" được viết trong tệp pom.xml của bạn hoặc được thêm vào Thư viện trong cài đặt dự án.

Nhân tiện, theo cách tương tự, bạn có thể giải quyết vấn đề không thể lưu trữ null cho các kiểu nguyên thủy. Nếu một cột trong bảng thuộc loại INT, thì có một số cách để lấy giá trị rỗng từ cột đó. Xem ví dụ dưới đây:

Integer id1 = results.getObject(8, Integer.class);    	 // this will work
Integer id2 = results.getObject(8, int.class);                 //this will also work
int id3 = results.getObject(8,  Integer.class);            	//method will return null, JVM will throw NPE
int id4 = results.getObject(8,  int.class);                    	//method will return null, JVM will throw NPE

Cài đặt múi giờ trong MySQL

Rất nhiều điều thú vị đã xảy ra với MySQL. Như bạn đã biết, khi tạo kết nối MySQL, bạn có thể thêm các tham số khác nhau vào đó :
mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning

Vì vậy, ba tham số đã được thêm vào để hoạt động với các múi giờ trong MySQL. Bạn có thể chuyển các tham số này khi thiết lập kết nối với máy chủ.

Dưới đây tôi sẽ đưa ra một bảng với họ:

Tham số giá trị Giá trị mặc định
kết nốiTimeZone ĐỊA PHƯƠNG | MÁY CHỦ | vùng người dùng MÁY CHỦ
forceConnectionTimeZoneToSession thật | SAI ĐÚNG VẬY
bảo tồnInstant thật | SAI SAI

Sử dụng tham số connectionTimeZone , chúng tôi chọn múi giờ (múi giờ) trong đó tất cả các yêu cầu sẽ được thực hiện. Từ quan điểm của khách hàng, máy chủ đang chạy trong múi giờ được chỉ định.

Tham số forceConnectionTimeZoneToSession khiến biến time_zone của phiên bị bỏ qua và được thay thế bằng connectionTimeZone.

Cuối cùng, tham sốserveInstants kiểm soát chuyển đổi thời gian chính xác giữa timeZone của JVM và connectionTimeZone.

Các cấu hình phổ biến nhất là:

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - tương ứng với trình điều khiển MySQL JDBC cũ phiên bản 5.1 với useLegacyDatetimeCode=true.

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=true là một chế độ mới cung cấp cách tự nhiên nhất để xử lý các giá trị ngày và giờ.

  • connectionTimeZone=SERVER &serveInstants=true - Tương ứng với trình điều khiển MySQL JDBC cũ phiên bản 5.1 với useLegacyDatetimeCode=false.

  • connectionTimeZone=user_define &preserveInstants=true - Giúp khắc phục tình trạng mà trình kết nối không thể nhận ra múi giờ của máy chủ vì múi giờ được đặt dưới dạng viết tắt chung chẳng hạn như CET/CEST.

Vâng, ngày tháng là một chủ đề thú vị và có nhiều vấn đề với chúng. Như người ta vẫn nói: tất nhiên là đáng sợ, nhưng tôi cũng không tức giận! :)