java.util.Date
vs java.sql.Date
: когда использовать, что и почему?
java.util.Date vs java.sql.Date
Ответ 1
Поздравляем, вы поразили мою любимую пизду с помощью 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: полностью.
Ответ 2
ПОСЛЕДНИЙ РЕДАКТ. Начиная с 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 при их передаче, для безопасности типов и документации.
Ответ 3
Единственный раз, когда вы используете java.sql.Date
находится в java.sql.Date
PreparedStatement.setDate
. В противном случае используйте java.util.Date
. Он сообщает, что ResultSet.getDate
возвращает java.sql.Date
но он может быть назначен непосредственно java.util.Date
.
Ответ 4
У меня была одна и та же проблема, самый простой способ, который я нашел, чтобы вставить текущую дату в подготовленный оператор, таков:
preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Ответ 5
Класс 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());
}