java.util.Date vs java.sql.Date

429

java.util.Date vs java.sql.Date: когда использовать, что и почему?

Теги:
datetime
date
jdbc

5 ответов

492
Лучший ответ

Поздравляем, вы поразили мою любимую пизду с помощью JDBC: обработка класса даты.

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

  • java.sql.Date соответствует 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-запросах и будут переносимыми из базы данных в другую и позволит вам избежать этой части API JDBC/Java Date: полностью.

  • 15
    Хороший ответ. Но не слишком ли недружелюбно хранить даты для DBA?
  • 24
    Возможно, однако, что администраторы баз данных, как правило, предпочитают свои СУБД и отвергают все, что не относится к этой СУБД напрямую ( я смотрю на вас, поклонники Oracle ), в то время как ожидается, что приложения Java будут работать со всеми из них. Лично я вообще не люблю помещать свою логику в БД.
Показать ещё 6 комментариев
50

ПОСЛЕДНИЙ РЕДАКТ. Начиная с 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 не все так плохо использовать для полей timestamp в объектах, хотя я сам обычно обертываю их в juD при их передаче, для безопасности типов и документации.

  • 5
    Почему мы должны предпочитать java.time вместо java.sql при хранении в базе данных? Мне действительно интересно в заявлении, но я хочу понять, почему :)
17

Единственный раз, когда вы используете java.sql.Date находится в java.sql.Date PreparedStatement.setDate. В противном случае используйте java.util.Date. Он сообщает, что ResultSet.getDate возвращает java.sql.Date но он может быть назначен непосредственно java.util.Date.

  • 3
    Эх, ResultSet # getDate () возвращает sql.Date (который расширяет util.Date).
  • 3
    @Esko - "Эмм", я исправил это до того, как вы прокомментировали (и понизили).
Показать ещё 2 комментария
9

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

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
  • 1
    не могу получить часовой пояс с этим.
2

Класс java.util.Date в Java представляет конкретный момент времени (e,.g., Ноябрь 25 16:30:45 до миллисекунд), но тип данных DATE в БД представляет только дату (например, Ноябрь 25). Чтобы вы не предоставили объекту java.util.Date DB по ошибке, 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());
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню