java.util.Date
против java.sql.Date
: когда использовать, что и почему?
java.util.Date
против java.sql.Date
: когда использовать, что и почему?
Ответы:
Поздравляю, вы ударили мою любимую любимую мозоль с 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.
ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ: Начиная с 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 при их передаче, для обеспечения безопасности типов и в качестве документации.
java.sql.Date
с PreparedStatement
etc! Но когда вы передаете его, используйте тот, LocalDate
который вы конвертируете, используя java.sql.Date.valueOf
и java.sql.Date.valueOf
при настройке, и конвертируйте его обратно как можно раньше, используя java.sql.Date.toLocalDate
- опять же, потому что вы хотите задействовать java.sql как можно меньше, и потому что он изменчив.
Единственное время для использования java.sql.Date
в PreparedStatement.setDate
. В противном случае используйте java.util.Date
. Это говорит о том, что ResultSet.getDate
возвращает a, java.sql.Date
но его можно назначить непосредственно a java.util.Date
.
java.sql.Date
может быть назначено, java.util.Date
когда первое расширяет второе? Какой смысл вы пытаетесь сделать?
У меня была та же проблема, самый простой способ вставить текущую дату в подготовленное заявление - это следующий:
preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
Не используйте ни
java.time.Instant
Заменяет java.util.Date
java.time.LocalDate
Заменяет java.sql.Date
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
set 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.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.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());
}
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
если они вызываются, как очевидно из подробностей их реализации.