java.util.Date vs java.sql.Date

501

java.util.Dateпротив 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-time plug ). Один хакерский способ, который можно сделать, - это сохранить компонент даты как один компонент времени и времени как другой, например, прямо сейчас будет 20100221 и 154536123. Эти магические числа можно использовать в запросах SQL, и они будут переносимы из базы данных в другую и позволит вам полностью избежать этой части JDBC / Java Date API.

Эско
источник
17
Хороший ответ. Но не слишком ли недружелюбно хранить даты для DBA?
Черувим
26
Возможно, однако, что администраторы баз данных, как правило, предпочитают свои СУБД и отвергают все, что не относится к этой СУБД напрямую ( я смотрю на вас, поклонники Oracle ), в то время как ожидается, что приложения Java будут работать со всеми из них. Лично я вообще не люблю помещать свою логику в БД.
Esko
2
Мой столбец mysql - это datetime, но я выполняю ps.setDate (new java.sql.Date (myObject.getCreatedDate (). GetTime ())); Я теряю часть миллисекунд, как это исправить?
Blankman
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- при использовании библиотек, которые его используют. В противном случае, как можно меньше, по нескольким причинам:

  • Это изменчиво, что означает, что вы должны делать защитную копию каждый раз, когда передаете или возвращаете из метода.

  • Он не очень хорошо справляется с датами, что, на мой взгляд, должно быть задом наперед людям, подобным вашей.

  • Теперь, потому что Джуд не выполняет свою работу очень хорошо, Calendarбыли введены ужасные классы. Они также изменчивы и с ними ужасно работать, и их следует избегать, если у вас нет выбора.

  • Существуют лучшие альтернативы, такие как Joda Time API ( который может даже превратиться в Java 7 и стать новым официальным API для обработки дат - быстрый поиск говорит, что этого не произойдет).

Если вы чувствуете, что излишне вводить новую зависимость, такую ​​как Joda, longне так уж и плохо использовать поля для отметок времени в объектах, хотя я сам обычно оборачиваю их в juD при их передаче, для обеспечения безопасности типов и в качестве документации.

gustafc
источник
5
Почему мы должны предпочитать java.time вместо java.sql при хранении в базе данных? Я действительно интересен в заявлении, но я хочу понять, почему :)
Жан-Франсуа Савар
@ Jean-FrançoisSavard Я надеюсь, что вы нашли ответ на свой вопрос с момента публикации этого комментария, но вот ответ, просто ради полноты: все еще в порядке java.sql.Date с PreparedStatementetc! Но когда вы передаете его, используйте тот, 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
Эх, ResultSet # getDate () возвращает sql.Date (который расширяет util.Date).
Esko
3
@Esko - "Эмм", я исправил это прежде, чем вы прокомментировали (и понизили).
Пол Томблин
1
Важно отметить, что причина, по которой java.sql.Date может быть назначен java.util.Date, заключается в том, что первый является подклассом второго.
dj18
2
Почему это «говорит», что a java.sql.Dateможет быть назначено, java.util.Dateкогда первое расширяет второе? Какой смысл вы пытаетесь сделать?
Маркиз Лорн
11

У меня была та же проблема, самый простой способ вставить текущую дату в подготовленное заявление - это следующий:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Israelm
источник
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 . Для большей гибкости используйте OffsetDateTimeset 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 Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

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

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии . Нет необходимости в строках, нет необходимости вjava.sql.* классах.

Где взять классы java.time?

Таблица какой библиотеки java.time использовать с какой версией Java или Android

Базилик Бурк
источник
1
Upvote для замены ссылки чумы вирусом короны. Более подходящим сейчас.
ankuranurag2
2

Класс java.util.Date в Java представляет определенный момент времени (например, .g., 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), но выдает исключение, если вы попытаетесь задать его часами (чтобы реализовать его концепцию только дата). Ожидается, что драйвер БД распознает этот класс и просто использует 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если они вызываются, как очевидно из подробностей их реализации.

Абдул Алим Шакир
источник