สถานการณ์ปัจจุบันเมื่อเวลาผ่านไป

นับตั้งแต่เวลาที่ JDBC ถูกประดิษฐ์ขึ้นและอินเทอร์เฟซได้รับมาตรฐาน 20 ปีผ่านไป และในช่วงเวลานี้ มีหลายสิ่งหลายอย่างเปลี่ยนไป

ประการแรก โลกกลายเป็นสากลและตอนนี้เซิร์ฟเวอร์เดียวสามารถให้บริการผู้ใช้จากทั่วทุกมุมโลก ความเร็วอินเทอร์เน็ตเพิ่มขึ้น ดังนั้นจึงมีการเพิ่มชนิดข้อมูลอื่นใน SQLเพื่อทำงานกับเวลา ตอนนี้ประเภทมีลักษณะดังนี้:

  • DATE - เก็บวันที่: ปี, เดือน, วัน
  • TIME - จัดเก็บเวลา: ชั่วโมง นาที วินาที
  • TIMESTAMP - เก็บช่วงเวลาเฉพาะ: วันที่ เวลา และมิลลิวินาที
  • การประทับเวลาด้วยโซนเวลา - การประทับเวลาและโซนเวลา (ชื่อโซนหรือออฟเซ็ต)

ประการที่สอง Java เปิดตัว DateTime API สำหรับการจัดการเวลาส่วนกลาง มีคลาสดังต่อไปนี้:

  • วันที่และเวลา :
    • วันที่ท้องถิ่น
    • เวลาท้องถิ่น
  • ช่วงเวลาที่แน่นอน :
    • java.time.Instant
    • java.time.LocalDateTime
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime
  • เวลากับเขตเวลา :
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime

ประเด็นที่น่าสนใจประการที่สามคือไคลเอ็นต์ SQL จำนวนมากต้องการรับเวลาจากเซิร์ฟเวอร์ที่อยู่ในโลคัลโซนของตนอยู่แล้ว แน่นอนคุณสามารถแปลงเวลาได้ทันที แต่ไม่สะดวกและมีข้อผิดพลาด

ตัวอย่างเช่น ฉันต้องการรับงานทั้งหมดสำหรับวันนี้จากฐานข้อมูล SQL Server มี ฟังก์ชัน CURDATE()สำหรับสิ่งนี้ เฉพาะที่นี่เซิร์ฟเวอร์อยู่ในสหรัฐอเมริกา ส่วนฉันอยู่ที่ญี่ปุ่น และฉันอยากให้เขาคืนบันทึกทั้งหมดสำหรับ "วันนี้ของฉัน" ไม่ใช่ "วันนี้ของเขา"

โดยทั่วไป เซิร์ฟเวอร์ SQL จะต้องสามารถทำงานร่วมกับไคลเอ็นต์ในเขตเวลาต่างๆ ได้อย่างชาญฉลาด

ปัญหาสมัยใหม่ต้องการวิธีแก้ปัญหาที่ทันสมัย

โดยหลักการแล้ว ชนิดใหม่จาก Java DateTime API และชนิดจาก SQL สามารถแมปได้สะดวก เพื่อแสดงประเภทDATEใน Java คุณต้องใช้ คลาส java.time.LocalDateจาก JDK 8 DateTime API

ประเภทTIME จากฐานข้อมูลสามารถแสดงด้วยสองประเภทจาก Java: java.time.LocalTimeและjava.time.OffsetTime ไม่มีอะไรซับซ้อนเช่นกัน

จุดในเวลาที่ระบุซึ่งแสดงโดย ประเภท TIMESTAMPในฐานข้อมูล สามารถแสดงในรูปแบบ Java ได้ 4 ประเภท:

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

และสุดท้ายTIMESTAMP WITH TIME ZONEสามารถแสดงได้สองประเภท:

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

เนื่องจากคุณคุ้นเคยกับ DateTime API อยู่แล้ว การจำเรื่องนี้จึงไม่ใช่เรื่องยากสำหรับคุณ :)

ฉันจะเขียนเป็นตารางเพื่อให้ง่ายขึ้น:

ประเภท SQL ประเภทจาวา
วันที่ java.time.LocalDate
เวลา java.time.LocalTime
java.time.OffsetTime
ประทับเวลา java.time.Instant
java.time.LocalDateTime
java.time.OffsetDateTime
java.time.ZonedDateTime
ประทับเวลาด้วยโซนเวลา java.time.OffsetDateTime
_

รับวันที่

ฉันมีข่าวดีสำหรับคุณ ครั้งแรกในระยะเวลานาน เราสามารถหลีกเลี่ยงข้อ จำกัด ของ เมธอด getDate()ซึ่งส่งคืนประเภทวันที่ java.sql

ประเด็นอยู่ที่วัตถุชุดผลลัพธ์มีอีกวิธีที่น่าสนใจคือgetObject () วิธีการนี้ใช้พารามิเตอร์สองตัว: คอลัมน์และประเภท และส่งคืนค่าของคอลัมน์ที่แปลงเป็นประเภทที่กำหนด รูปแบบทั่วไปของวิธีการมีดังนี้:

ClassName Name = getObject(column, ClassName);

และถ้าคุณต้องการแปลง ประเภท DATEเป็น ประเภท java.time.LocalDateคุณต้องเขียนดังนี้:

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

และ TIMESTAMP โดยทั่วไปสามารถแปลงเป็นประเภทต่างๆ ได้มากมาย:

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

สำคัญ! รหัสนี้จะใช้งาน ไม่ได้หากคุณมีไดรเวอร์ MySQL JDBC ที่ล้าสมัย ให้ความสนใจกับเวอร์ชันของ "mysql-connector-java" ที่เขียนใน pom.xml ของคุณ หรือเพิ่มไปยัง Libraries ในการตั้งค่าโครงการ

ในทำนองเดียวกัน คุณสามารถเลี่ยงการไม่สามารถจัดเก็บค่าว่างสำหรับประเภทดั้งเดิมได้ หากคอลัมน์ตารางเป็นประเภท INT มีสองวิธีในการรับค่าว่างจากคอลัมน์นั้น ดูตัวอย่างด้านล่าง:

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

การตั้งค่าเขตเวลาใน MySQL

มีสิ่งที่น่าสนใจมากมายเกิดขึ้นกับ MySQL เช่นกัน ดังที่คุณทราบ เมื่อสร้างการเชื่อมต่อ MySQL คุณสามารถเพิ่มพารามิเตอร์ ต่างๆ เข้าไปได้ :
mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning

ดังนั้นจึงมีการเพิ่มพารามิเตอร์สามตัวเพื่อทำงานกับเขตเวลาใน MySQL คุณสามารถส่งพารามิเตอร์เหล่านี้ได้เมื่อคุณสร้างการเชื่อมต่อกับเซิร์ฟเวอร์

ด้านล่างฉันจะให้ตารางกับพวกเขา:

พารามิเตอร์ ค่า ค่าเริ่มต้น
การเชื่อมต่อโซนเวลา ท้องถิ่น | เซิร์ฟเวอร์ | โซนผู้ใช้ เซิร์ฟเวอร์
ForceConnectionTimeZoneToSession จริง | เท็จ จริง
รักษาทันที จริง | เท็จ เท็จ

การใช้ พารามิเตอร์ connectionTimeZoneเราเลือกโซนเวลา (โซนเวลา) ที่คำขอทั้งหมดจะถูกดำเนินการ จากมุมมองของลูกค้า เซิร์ฟเวอร์กำลังทำงานในเขตเวลาที่ระบุ

พารามิเตอร์forceConnectionTimeZoneToSessionทำให้ตัวแปร session time_zone ถูกละเว้นและแทนที่ด้วย connectionTimeZone

สุดท้าย พารามิเตอร์ reservedInstantsจะควบคุมการแปลงเวลาที่แน่นอนระหว่างเขตเวลาของ JVM และ connectionTimeZone

การกำหนดค่าที่พบบ่อยที่สุดคือ:

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - สอดคล้องกับไดรเวอร์ MySQL JDBC รุ่นเก่า 5.1 ที่มี useLegacyDatetimeCode=true

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=trueเป็นโหมดใหม่ที่มีวิธีจัดการกับค่าวันที่และเวลาอย่างเป็นธรรมชาติที่สุด

  • connectionTimeZone=SERVER & reservedInstants=true - สอดคล้องกับไดรเวอร์ MySQL JDBC รุ่นเก่า 5.1 ที่มี useLegacyDatetimeCode=false

  • connectionTimeZone=user_defined & reservedInstants=true - ช่วยแก้ไขสถานการณ์ที่ตัวเชื่อมต่อไม่รู้จักโซนเวลาของเซิร์ฟเวอร์ เนื่องจากถูกตั้งค่าเป็นตัวย่อทั่วไป เช่น CET/CEST

ใช่ วันที่เป็นหัวข้อที่น่าสนใจและมีปัญหามากมายกับพวกเขา ดังคำกล่าวที่ว่า: มันน่ากลัวแน่นอน แต่ฉันไม่โกรธด้วย! :)