java.util.Date vs java.sql.Date


คำตอบ:


585

ขอแสดงความยินดีคุณได้โจมตีสัตว์เลี้ยงตัวโปรดของฉันด้วย JDBC: การจัดการชั้นเรียนวันที่

ฐานข้อมูลโดยทั่วไปจะสนับสนุนอย่างน้อยสามรูปแบบของเขตข้อมูลวันที่และเวลาซึ่งเป็นวันที่เวลาและการประทับเวลา แต่ละเหล่านี้มีระดับที่สอดคล้องกันใน JDBC และแต่ละของพวกเขาขยาย java.util.Dateซีแมนทิกส์ด่วนของแต่ละสามรายการมีดังต่อไปนี้:

  • java.sql.Dateสอดคล้องกับ SQL DATE ซึ่งหมายความว่าจะเก็บปีเดือนและวันในขณะที่ละเว้นชั่วโมงนาทีวินาทีและมิลลิวินาที นอกจากนี้sql.Dateจะไม่เชื่อมโยงกับเขตเวลา
  • java.sql.Timeสอดคล้องกับ SQL เวลาและเป็นควรจะชัดเจนเพียงมีข้อมูลเกี่ยวกับชั่วโมงนาทีวินาทีและมิลลิวินาที
  • java.sql.Timestampสอดคล้องกับ SQL TIMESTAMP ซึ่งเป็นวันที่แน่นอนกับนาโนวินาที ( โปรดทราบว่าutil.Dateรองรับมิลลิวินาทีเท่านั้น! ) ด้วยความแม่นยำที่ปรับแต่งได้

หนึ่งในข้อบกพร่องที่พบบ่อยที่สุดเมื่อใช้ไดรเวอร์ JDBC ที่เกี่ยวข้องกับทั้งสามประเภทคือประเภทที่ได้รับการจัดการอย่างไม่ถูกต้อง ซึ่งหมายความว่าsql.Dateเป็นเขตเวลาที่เฉพาะเจาะจงsql.Timeประกอบด้วยปีปัจจุบันเดือนและวันและอื่น ๆ

ในที่สุด: อันไหนที่จะใช้?

ขึ้นอยู่กับชนิดของฟิลด์ SQL จริงๆ PreparedStatementมี setters สำหรับทั้งสามค่า#setDate()เป็นหนึ่งสำหรับsql.Date, #setTime()สำหรับsql.Timeและสำหรับ#setTimestamp()sql.Timestamp

โปรดทราบว่าถ้าคุณใช้ps.setObject(fieldIndex, utilDateObject);คุณสามารถให้util.Dateไดร์เวอร์ JDBC ปกติซึ่งปกติจะกินได้อย่างมีความสุขราวกับว่ามันเป็นประเภทที่ถูกต้อง แต่เมื่อคุณขอข้อมูลในภายหลังคุณอาจสังเกตเห็นว่าคุณขาดอะไรจริง ๆ

ฉันกำลังบอกว่าไม่ควรใช้วันที่เลย

สิ่งที่ฉันกำลังพูดที่ช่วยประหยัดมิลลิวินาที / nanoseconds เป็น longs ธรรมดาและแปลงให้เป็นวัตถุใด ๆ ที่คุณใช้ ( ปลั๊ก joda เวลาบังคับ ) วิธีแฮ็คหนึ่งที่สามารถทำได้คือการจัดเก็บองค์ประกอบวันที่เป็นองค์ประกอบหนึ่งที่ยาวและเวลาเป็นอีกหนึ่งตัวอย่างเช่นตอนนี้จะเป็น 20100221 และ 154536123 ตัวเลขมายากลเหล่านี้สามารถใช้ในแบบสอบถาม SQL และจะพกพาจากฐานข้อมูลไปยังอีกและ จะช่วยให้คุณหลีกเลี่ยงส่วนนี้ของ JDBC / Java Date API: s ทั้งหมด


17
คำตอบที่ดี แต่ไม่ได้จัดเก็บวันที่ไว้นานสำหรับ DBA ที่ไม่เป็นมิตรใช่ไหม
cherouvim

26
บางทีโดยทั่วไปแล้ว DBA: s มักจะเลือก RDBMS และปฏิเสธทุกอย่างที่ไม่เกี่ยวกับ RDBMS โดยตรง ( ฉันกำลังมองคุณแฟน Oracle ) ในขณะที่แอปพลิเคชัน Java คาดว่าจะทำงานกับพวกเขาทั้งหมด โดยส่วนตัวแล้วฉันไม่ชอบใส่ตรรกะลงในฐานข้อมูลเลย
Esko

2
คอลัมน์ mysql ของฉันคือ datetime แต่ทำ ps.setDate (ใหม่ java.sql.Date (myObject.getCreatedDate (). getTime ())); ฉันกำลังสูญเสียส่วนมิลลิวินาทีวิธีการแก้ไขปัญหานี้?
Blankman

2
เพื่อไม่ให้สูญเสียมิลลิวินาทีของคุณ: new java.sql.Timestamp (utilDate.getTime ())
Kieveli

3
ฉันบอกว่ามันเป็นเรื่องธรรมดา ข้อผิดพลาดที่เฉพาะเจาะจงของ TZ ในขณะที่สเปคไม่ควรเป็น
Esko

60

LATE EDIT:เริ่มต้นด้วย Java 8 คุณไม่ควรใช้java.util.Dateหรือ java.sql.Dateถ้าคุณสามารถหลีกเลี่ยงได้และควรใช้java.timeแพ็คเกจ (ตาม Joda) แทนการใช้อย่างอื่นแทน หากคุณไม่ได้ใช้ Java 8 ต่อไปนี้เป็นคำตอบดั้งเดิม:


java.sql.Date- เมื่อคุณเรียกเมธอด / ตัวสร้างของไลบรารีที่ใช้มัน (เช่น JDBC) ไม่อย่างอื่น คุณไม่ต้องการแนะนำการพึ่งพาไลบรารีฐานข้อมูลสำหรับแอ็พพลิเคชัน / โมดูลที่ไม่ได้จัดการกับ JDBC อย่างชัดเจน

java.util.Date- เมื่อใช้ไลบรารีที่ใช้งาน มิฉะนั้นน้อยที่สุดด้วยเหตุผลหลายประการ:

  • มันไม่แน่นอนซึ่งหมายความว่าคุณต้องทำสำเนาการป้องกันไว้ทุกครั้งที่คุณส่งหรือส่งคืนจากวิธีการ

  • มันไม่สามารถจัดการกับวันที่ได้เป็นอย่างดีซึ่งคนหลังเหมือนคุณอย่างแท้จริงคิดว่าการจัดการชั้นเรียนวันที่ควร

  • ตอนนี้เนื่องจาก juD ทำงานได้ไม่ดีนักCalendarชั้นเรียนที่น่ากลัวจึงถูกนำมาใช้ พวกเขายังไม่แน่นอนและน่ากลัวที่จะทำงานด้วยและควรหลีกเลี่ยงหากคุณไม่มีทางเลือก

  • มีทางเลือกที่ดีกว่าเช่น Joda Time API ( ซึ่งอาจทำให้เป็น Java 7 และกลายเป็น API จัดการวันที่ใหม่อย่างเป็นทางการ - การค้นหาอย่างรวดเร็วบอกว่าจะไม่)

หากคุณรู้สึกว่าเกินความสามารถในการแนะนำการพึ่งพาใหม่เช่น Joda longไม่ใช่สิ่งที่ไม่ดีเลยที่จะใช้สำหรับเขตข้อมูลประทับเวลาในวัตถุแม้ว่าตัวฉันเองมักจะห่อมันไว้ใน juD เมื่อผ่านมันไปเพื่อความปลอดภัยประเภทและเป็นเอกสาร


5
ทำไมเราควรเลือก java.time มากกว่า java.sql เมื่อจัดเก็บในฐานข้อมูล ฉันน่าสนใจจริงๆในแถลงการณ์ แต่ฉันต้องการที่จะเข้าใจว่าทำไม :)
Jean-François Savard

@ Jean-FrançoisSavardฉันหวังว่าคุณจะได้พบคำตอบสำหรับคำถามของคุณตั้งแต่คุณโพสต์ความคิดเห็นนั้น - แต่นี่คือคำตอบเพียงเพื่อความสมบูรณ์: เพราะมันสมบูรณ์แบบที่ตกลงjava.sql.Date กับPreparedStatementอื่น ๆ ! แต่เมื่อคุณผ่านมันไปให้ใช้สิ่งLocalDateที่คุณแปลงโดยใช้java.sql.Date.valueOfและjava.sql.Date.valueOfเมื่อทำการตั้งค่าและแปลงมันกลับมาให้เร็วที่สุดเท่าที่จะทำได้โดยใช้java.sql.Date.toLocalDate อีกครั้งเพราะคุณต้องการที่จะใช้ java.sql ให้น้อยที่สุดเท่าที่จะทำได้
gustafc

19

เวลาเท่านั้นที่จะใช้งานอยู่ในjava.sql.Date มิฉะนั้นการใช้งานPreparedStatement.setDate java.util.Dateมันบอกว่าResultSet.getDateผลตอบแทนแต่ก็สามารถได้รับมอบหมายโดยตรงกับjava.sql.Datejava.util.Date


3
Ehm, ResultSet # getDate () ส่งคืน sql.Date (ซึ่งขยายการใช้ util วันที่)
Esko

3
@Esko - "Ehm" ฉันได้แก้ไขก่อนที่คุณจะแสดงความคิดเห็น (และลดลง)
พอลทอมบลิน

1
สิ่งสำคัญคือให้สังเกตว่าเหตุผลที่สามารถกำหนด java.sql.Date ให้กับ java.util.Date นั้นเป็นเพราะตัวแรกเป็นซับคลาสของวินาที
dj18

2
ทำไมมันจึง 'บอก' ว่า a java.sql.Dateสามารถถูกกำหนดให้กับ a java.util.Dateเมื่ออดีตขยายส่วนหลัง? ประเด็นที่คุณพยายามทำคืออะไร?
มาร์ควิสแห่ง Lorne

11

ฉันมีปัญหาเดียวกันวิธีที่ง่ายที่สุดที่ฉันพบเพื่อแทรกวันที่ปัจจุบันลงในคำสั่งที่เตรียมไว้คือ:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));

2
ไม่สามารถรับเขตเวลาด้วยสิ่งนี้
erhanasikoglu

4

TL; DR

ไม่ใช้

ทั้ง

java.util.Date vs java.sql.Date: จะใช้เมื่อใดและเพราะเหตุใด

ทั้งสองคลาสนี้แย่มากข้อบกพร่องในการออกแบบและการนำไปใช้งาน หลีกเลี่ยงเช่นภัยพิบัติ Coronavirus

ใช้คลาสjava.timeแทนซึ่งกำหนดไว้ใน JSR 310 คลาสเหล่านี้เป็นกรอบงานชั้นนำของอุตสาหกรรมสำหรับการทำงานกับการจัดการวันที่ แทนที่เหล่านี้อย่างสิ้นเชิงเลือดเรียนมรดกอันยิ่งใหญ่เช่นDate, Calendar, SimpleDateFormatและเช่น

java.util.Date

ครั้งแรกjava.util.Dateหมายถึงการแสดงช่วงเวลาใน UTC หมายถึงการชดเชยจาก UTC เป็นศูนย์ชั่วโมง - นาที - วินาที

java.time.Instant

java.time.Instantตอนนี้ถูกแทนที่ด้วย

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

java.time.OffsetDateTime

Instantเป็นพื้นฐานระดับอาคารบล็อกของjava.time เพื่อความยืดหยุ่นมากขึ้นให้ใช้การOffsetDateTimeตั้งค่าเพื่อZoneOffset.UTCจุดประสงค์เดียวกัน: แสดงช่วงเวลาใน UTC

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

คุณสามารถส่งวัตถุนี้ไปยังฐานข้อมูลโดยใช้PreparedStatement::setObjectกับJDBC 4.2 หรือใหม่กว่า

myPreparedStatement.setObject(  , odt ) ;

กู้

OffsetDateTime odt = myResultSet.getObject(  , OffsetDateTime.class ) ;

java.sql.Date

java.sql.Dateชั้นยังเป็นที่น่ากลัวและล้าสมัย

คลาสนี้มีไว้เพื่อแสดงถึงวันที่เท่านั้นโดยไม่มีช่วงเวลาของวันและไม่มีเขตเวลา น่าเสียดายที่การแฮ็กของการออกแบบแย่มากคลาสนี้สืบทอดมาจากjava.util.Dateช่วงเวลาหนึ่ง (วันที่มีเวลาใน UTC) ดังนั้นคลาสนี้ทำท่าว่าจะเป็นวันที่เท่านั้นในขณะที่มีการชดเชยเวลาของวันและโดยปริยายของ UTC ทำให้เกิดความสับสนอย่างมาก อย่าใช้คลาสนี้

java.time.LocalDate

ให้ใช้java.time.LocalDateเพื่อติดตามเฉพาะวันที่ (ปี, เดือน, วันของเดือน) โดยไม่มีการแจ้งเตือนเวลาหรือเขตเวลาใด ๆ หรือออฟเซ็ต

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate ld = LocalDate.now( z ) ;    // Capture the current date as seen in the wall-clock time used by the people of a particular region (a time zone).

ส่งไปยังฐานข้อมูล

myPreparedStatement.setObject(  , ld ) ;

กู้

LocalDate ld = myResultSet.getObject(  , LocalDate.class ) ;

ตารางประเภทวันที่ - เวลาใน Java (ทั้งแบบดั้งเดิมและสมัยใหม่) และใน SQL มาตรฐาน


เกี่ยวกับjava.time

java.timeกรอบถูกสร้างขึ้นใน Java 8 และต่อมา ชั้นเรียนเหล่านี้แย่งลำบากเก่ามรดกเรียนวันที่เวลาเช่นjava.util.Date, และCalendarSimpleDateFormat

ต้องการเรียนรู้เพิ่มเติมโปรดดูที่ออราเคิลกวดวิชา และค้นหา Stack Overflow สำหรับตัวอย่างและคำอธิบายมากมาย สเปกJSR 310

Joda เวลาโครงการขณะนี้อยู่ในโหมดการบำรุงรักษาให้คำแนะนำแก่การโยกย้ายไปยังjava.timeชั้นเรียน

คุณสามารถแลกเปลี่ยนวัตถุjava.timeโดยตรงกับฐานข้อมูลของคุณ ใช้ไดรเวอร์ JDBC ที่สอดคล้องกับJDBC 4.2หรือใหม่กว่า ไม่จำเป็นต้องใช้สตริงไม่จำเป็นต้องใช้java.sql.*คลาส

จะรับคลาส java.time ได้ที่ไหน?

  • Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11และใหม่กว่า - ส่วนหนึ่งของ Java API มาตรฐานที่มีการใช้งานแบบรวม
    • Java 9 เพิ่มคุณสมบัติและการแก้ไขเล็กน้อย
  • Java SE 6และ Java SE 7
    • ส่วนใหญ่ของjava.timeการทำงานจะกลับรังเพลิง Java 6 และ 7 ในThreeTen-ย้ายกลับ
  • Android

ตารางที่ไลบรารี java.time ใดที่จะใช้กับเวอร์ชันของ Java หรือ Android


1
โหวตขึ้นเพื่อแทนที่ Plague Reference โดย Corona virus น่าเชื่อถือมากขึ้นในขณะนี้
ankuranurag2

2

คลาส java.util.Date ใน Java แสดงช่วงเวลาเฉพาะ (e, .g., 2013 25 พฤศจิกายน 16:30:45 น. เป็นมิลลิวินาที) แต่ประเภทข้อมูล DATE ใน DB แสดงวันที่เท่านั้น (เช่น 2013 พ.ย. 25) เพื่อป้องกันไม่ให้คุณให้วัตถุ java.util.Date กับฐานข้อมูลโดยไม่ได้ตั้งใจ Java ไม่อนุญาตให้คุณตั้งค่าพารามิเตอร์ SQL เป็น java.util.Date โดยตรง:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work

แต่มันยังช่วยให้คุณสามารถทำเช่นนั้นได้โดยการบังคับ / เจตนา (จากนั้นโปรแกรมควบคุม DB จะไม่สนใจชั่วโมงและนาที) สิ่งนี้ทำกับคลาส java.sql.Date:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work

วัตถุ java.sql.Date สามารถเก็บช่วงเวลา (เพื่อให้ง่ายต่อการสร้างจาก java.util.Date) แต่จะส่งข้อยกเว้นถ้าคุณพยายามขอชั่วโมง (เพื่อบังคับใช้แนวคิดในการเป็น วันที่เท่านั้น) คาดว่าไดรเวอร์ DB จะรู้จักคลาสนี้และเพียงแค่ใช้ 0 สำหรับชั่วโมง ลองสิ่งนี้:

public static void main(String[] args) {
  java.util.Date d1 = new java.util.Date(12345);//ms since 1970 Jan 1 midnight
  java.sql.Date d2 = new java.sql.Date(12345);
  System.out.println(d1.getHours());
  System.out.println(d2.getHours());
}

0

java.util.Dateแสดงถึงเวลาที่กำหนดโดยทันทีด้วยความแม่นยำมิลลิวินาที มันแสดงให้เห็นถึงข้อมูลวันที่และเวลาที่ไม่มีเขตเวลา คลาส java.util.Date ใช้อินเตอร์เฟสแบบ Serializable, Cloneable และ Comparable มันเป็นกรรมพันธุ์ด้วยjava.sql.Date, java.sql.Timeและjava.sql.Timestampอินเตอร์เฟซ

java.sql.Dateขยายคลาส java.util.Date ซึ่งแสดงวันที่โดยไม่มีข้อมูลเวลาและควรใช้เฉพาะเมื่อจัดการกับฐานข้อมูล เพื่อให้สอดคล้องกับคำจำกัดความของ SQL DATE ค่ามิลลิวินาทีที่ห่อด้วยjava.sql.Dateอินสแตนซ์จะต้องเป็น 'ปกติ' โดยการตั้งค่าชั่วโมงนาทีวินาทีและมิลลิวินาทีเป็นศูนย์ในเขตเวลาเฉพาะที่เกี่ยวข้องกับอินสแตนซ์

มันสืบทอดวิธีการสาธารณะทั้งหมดjava.util.Dateเช่นgetHours(), getMinutes(), getSeconds(), setHours(), ,setMinutes() setSeconds()ตามที่java.sql.Dateไม่ได้เก็บข้อมูลเวลามันจะแทนที่การดำเนินการเวลาjava.util.Dateทั้งหมดและวิธีการทั้งหมดเหล่านี้จะดำเนินการjava.lang.IllegalArgumentExceptionหากเรียกใช้ตามที่เห็นได้ชัดจากรายละเอียดการใช้งาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.