Keadaan semasa dari semasa ke semasa

Sejak masa JDBC dicipta dan antara mukanya diseragamkan, 20 tahun telah berlalu, dan pada masa ini banyak perkara telah berubah.

Pertama, dunia telah menjadi global dan kini satu pelayan boleh memberi perkhidmatan kepada pengguna dari seluruh dunia. Kelajuan internet meningkat. Oleh itu, jenis data lain telah ditambahkan pada SQL untuk berfungsi dengan masa. Sekarang jenis kelihatan seperti ini:

  • DATE - menyimpan tarikh: tahun, bulan, hari.
  • MASA - menyimpan masa: jam, minit, saat.
  • TIMESTAMP - menyimpan titik masa tertentu: tarikh, masa dan milisaat.
  • TIMESTAMP DENGAN ZON MASA - TIMESTAMP dan zon masa (nama zon atau offset).

Kedua, Java memperkenalkan API DateTime untuk pengurusan masa global. Ia mempunyai kelas berikut:

  • Tarikh dan masa :
    • LocalDate
    • LocalTime
  • Detik yang tepat :
    • java.time.Instant
    • java.time.LocalDateTime
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime
  • Masa dengan zon waktu :
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime

Perkara ketiga yang menarik ialah ramai pelanggan SQL ingin menerima masa daripada pelayan yang sudah berada di zon tempatan mereka . Sudah tentu, anda boleh menukar masa dengan cepat, tetapi ia tidak mudah, dan terdapat kesilapan.

Sebagai contoh, saya ingin mendapatkan semua tugasan untuk hari ini daripada pangkalan data. SQL Server mempunyai fungsi CURDATE() untuk ini. Hanya di sini pelayan berada di Amerika Syarikat, dan saya di Jepun. Dan saya ingin dia mengembalikan semua rekod untuk "hari ini saya", dan bukan "hari ini".

Secara umum, pelayan SQL juga mesti dapat bekerja dengan bijak dengan pelanggan dalam zon waktu yang berbeza.

Masalah moden memerlukan penyelesaian moden

Pada dasarnya, jenis baharu daripada Java DateTime API dan jenis daripada SQL boleh dipetakan dengan mudah. Untuk mewakili jenis DATE dalam Java, anda perlu menggunakan kelas java.time.LocalDate daripada API DateTime JDK 8.

Jenis TIME daripada pangkalan data boleh diwakili oleh dua jenis daripada Java: java.time.LocalTime dan java.time.OffsetTime . Tiada yang rumit juga.

Titik masa tertentu, yang diwakili oleh jenis TIMESTAMP dalam pangkalan data, boleh diwakili dalam Java dengan 4 jenis:

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

Dan akhirnya, TIMESTAMP DENGAN ZON MASA boleh diwakili oleh dua jenis:

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

Memandangkan anda sudah biasa dengan API DateTime, mengingati perkara ini tidak sukar untuk anda :)

Saya akan menulisnya dalam bentuk jadual, jadi lebih mudah:

JENIS SQL Jenis Java
TARIKH java.time.LocalDate
MASA java.time.LocalTime
java.time.OffsetTime
STAMP MASA java.time.Java.time.LocalDateTime java.time.OffsetDateTime java.time.ZonedDateTime
_

STAMP MASA DENGAN ZON MASA java.time.OffsetDateTime
_

Mendapat tarikh

Saya ada berita baik untuk awak. Pertama dalam masa yang lama. Kita boleh mengatasi had kaedah getDate() , yang mengembalikan jenis Tarikh java.sql.

Intinya ialah objek ituset keputusanterdapat satu lagi kaedah yang menarik - getObject() . Kaedah ini mengambil dua parameter: lajur dan jenis, dan mengembalikan nilai lajur yang ditukar kepada jenis yang diberikan. Bentuk umum kaedah adalah seperti berikut:

ClassName Name = getObject(column, ClassName);

Dan jika anda ingin menukar jenis DATE kepada jenis java.time.LocalDate , maka anda perlu menulis sesuatu seperti:

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

Dan mana-mana TIMESTAMP secara umum boleh ditukar kepada sekumpulan jenis:

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);

Penting! Kod ini tidak akan berfungsi jika anda mempunyai Pemacu JDBC MySQL yang sudah lapuk . Beri perhatian kepada versi "mysql-connector-java" yang ditulis dalam pom.xml anda, atau ditambahkan pada Perpustakaan dalam tetapan projek.

Dengan cara yang sama, dengan cara yang sama, anda boleh mengatasi ketidakupayaan untuk menyimpan null untuk jenis primitif. Jika lajur jadual adalah jenis INT, maka terdapat beberapa cara untuk mendapatkan nol daripadanya. Lihat contoh di bawah:

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

Tetapan zon waktu dalam MySQL

Banyak perkara menarik berlaku dengan MySQL juga. Seperti yang anda ketahui, apabila membuat sambungan MySQL, anda boleh menambah pelbagai parameter kepadanya :
mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning

Jadi, tiga parameter telah ditambah untuk berfungsi dengan zon waktu dalam MySQL. Anda boleh lulus parameter ini apabila anda membuat sambungan ke pelayan.

Di bawah saya akan memberikan jadual dengan mereka:

Parameter Nilai Nilai asal
connectionTimeZone TEMPATAN | SERVER | zon pengguna SERVER
forceConnectionTimeZoneToSession benar | salah benar
preserveInstant benar | salah salah

Menggunakan parameter connectionTimeZone , kami memilih zon waktu (zon masa) di mana semua permintaan akan dilaksanakan. Dari sudut pandangan pelanggan, pelayan sedang berjalan dalam zon waktu yang ditentukan.

Parameter forceConnectionTimeZoneToSession menyebabkan pembolehubah zon masa sesi diabaikan dan digantikan dengan connectionTimeZone.

Akhir sekali, parameter preserveInstant mengawal penukaran masa tepat antara Zon masa JVM dan ZonMasa sambungan.

Konfigurasi yang paling biasa ialah:

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - sepadan dengan pemacu MySQL JDBC lama versi 5.1 dengan useLegacyDatetimeCode=true.

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=true ialah mod baharu yang menyediakan cara paling semula jadi untuk mengendalikan nilai tarikh dan masa.

  • connectionTimeZone=SERVER & preserveInstants=true - Sepadan dengan pemacu MySQL JDBC lama versi 5.1 dengan useLegacyDatetimeCode=false.

  • connectionTimeZone=user_defined & preserveInstants=true - Membantu mengatasi situasi di mana zon waktu pelayan tidak dapat dikenali oleh penyambung kerana ia ditetapkan sebagai singkatan generik seperti CET/CEST.

Ya, tarikh adalah topik yang menarik dan terdapat banyak masalah dengannya. Seperti kata pepatah: ia menakutkan, sudah tentu, tetapi saya juga tidak marah! :)