java.util.Date vs java.sql.Date


Відповіді:


585

Вітаємо, ти вдарив мого улюбленого вихованця за допомогою JDBC: Обробка класу побачень.

В основному бази даних зазвичай підтримують щонайменше три форми полів дати, які є датою, часом та позначкою часу. Кожен з них має відповідний клас у JDBC, і кожен з них поширюєтьсяjava.util.Date . Коротка семантика кожного з цих трьох:

  • java.sql.Dateвідповідає SQL DATE, що означає, що він зберігає роки, місяці та дні, тоді як година, хвилина, секунда та мілісекунда ігноруються. Крім того sql.Date, не прив’язаний до часових поясів.
  • java.sql.Timeвідповідає SQL TIME і, як має бути очевидно, містить лише інформацію про годину, хвилини, секунди та мілісекунди .
  • java.sql.Timestampвідповідає SQL TIMESTAMP, який є точною датою наносекунди ( зауважте, що util.Dateпідтримує лише мілісекунди! ) з настроюваною точністю.

Однією з найпоширеніших помилок при використанні драйверів JDBC стосовно цих трьох типів є те, що типи обробляються неправильно. Це означає, що sql.Dateзалежить від часового поясу, sql.Timeмістить поточний рік, місяць та день тощо.

Нарешті: який з них використовувати?

Дійсно, залежить від типу SQL поля. PreparedStatementмає задачі для всіх трьох значень, #setDate()будучи одним для sql.Date, #setTime()для sql.Timeі #setTimestamp()для sql.Timestamp.

Зверніть увагу, що якщо ви користуєтесь, ps.setObject(fieldIndex, utilDateObject);ви можете фактично дати нормальним util.Dateдля більшості драйверів JDBC, які з радістю пожирають його як би правильного типу, але коли ви запитаєте дані після цього, ви можете помітити, що ви насправді відсутні.

Я справді кажу, що жоден з Дат взагалі не повинен використовуватися.

Що я говорю, що збережіть мілісекунди / наносекунди як прості і перетворите їх на будь-які об'єкти, якими ви користуєтесь ( обов'язковий штекер joda-часу ). Один з химерних способів, який можна зробити, - це зберігати компонент дати як один тривалий і часовий компонент як інший, наприклад, зараз це буде 20100221 та 154536123. Ці магічні числа можна використовувати в SQL-запитах і переноситимуться з бази даних до іншої та дозволить вам уникнути цієї частини JDBC / Java Date API: s повністю.


17
Гарна відповідь. Але чи не зберігання дат так довго непривітно для DBA?
cherouvim

26
Можливо, проте DBA: s, як правило, схильні до свого вибору RDBMS і відкидають все, що не стосується саме RDBMS ( я дивлюся на вас, шанувальники Oracle ), тоді як програми Java повинні працювати з усіма ними. Особисто мені зовсім не подобається вкладати свою логіку в БД.
Есько

2
Мій стовпчик mysql - це дата, але робить ps.setDate (новий java.sql.Date (myObject.getCreateDate (). GetTime ())); Я втрачаю частину мілісекунд, як це виправити?
Бланкмен

2
Щоб не втратити свої мілісекунди: новий java.sql.Timestamp (utilDate.getTime ())
Kieveli

3
Я згадував, що це звичайна помилка, що вона є специфічною для TZ, тоді як її за специфікацією не повинно бути.
Есько

60

ПОСЛІДНИЙ ЕДИТ: Починаючи з 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, longs не все так погано використовувати для полів часових міток в об'єктах, хоча я зазвичай обертаю їх у juD, коли передавати їх навколо, для безпеки типу та як документації.


5
Чому ми повинні віддавати перевагу java.time над java.sql при зберіганні в базі даних? Мені дуже цікаво в заяві, але я хочу зрозуміти, чому :)
Жан-Франсуа Савар

@ Жан-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повертає a, java.sql.Dateале його можна призначити безпосередньо a java.util.Date.


3
Ehm, ResultSet # getDate () повертає sql.Date (який розширює util.Date).
Есько

3
@Esko - "Ем", я це виправдав, перш ніж ви коментували (і зволікали).
Пол Томблін

1
Важливо зауважити, що причина java.sql.Date може бути призначена java.util.Date тому, що перший - це підклас другого.
dj18

2
Чому так "говорять", що а java.sql.Dateможе бути призначений a, java.util.Dateколи перший розширює другий? Який сенс ти намагаєшся зробити?
Маркіз Лорн

11

У мене був той самий випуск, найпростіший спосіб, який я знайшов, щоб вставити поточну дату в підготовлену заяву, це такий:

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

2
не можу отримати часовий пояс з цим.
erhanasikoglu

4

тл; д-р

Не використовуйте жодного.

Ні

java.util.Date vs java.sql.Date: коли використовувати і для чого?

Обидва ці класи жахливі, недоліки в дизайні та реалізації. Уникайте, як коронавірус чуми .

Замість цього використовуйте класи 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, Calendar, і SimpleDateFormat.

Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - JSR 310 .

Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти до класів java.time .

Ви можете обмінюватися об'єктами java.time безпосередньо зі своєю базою даних. Використовуйте драйвер JDBC, сумісний з JDBC 4.2 або пізнішої версії. Немає необхідності в струнах, немає потреби в java.sql.*заняттях.

Де отримати класи java.time?

Таблиця, яку бібліотеку java.time використовувати в якій версії Java або Android


1
Заявка на заміну посилання на чуму вірусом Corona. Більше відносно зараз.
ankuranurag2

2

Клас java.util.Date в Java представляє певний момент часу (наприклад, 2013 р., 25 листопада, 16:30:45, до мілісекунд), але тип даних DATE в БД представляє лише дату (наприклад, 2013 р., 25 листопада). Щоб запобігти наданню об'єкта java.util.Date в БД помилково, Java не дозволяє встановлювати параметр SQL в java.util.Date безпосередньо:

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

Але це все ще дозволяє зробити це силою / наміром (тоді драйвер БД буде ігнорувати години та хвилини). Це робиться з класом 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), але викине виняток, якщо ви спробуєте запитати його протягом годин (щоб застосувати його концепцію бути a тільки дата). Очікується, що драйвер БД розпізнає цей клас і просто використовувати 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якщо викликаються як очевидні з деталей їхньої реалізації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.