Відповіді:
Вітаємо, ти вдарив мого улюбленого вихованця за допомогою 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 повністю.
ПОСЛІДНИЙ ЕДИТ: Починаючи з 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
s не все так погано використовувати для полів часових міток в об'єктах, хоча я зазвичай обертаю їх у juD, коли передавати їх навколо, для безпеки типу та як документації.
java.sql.Date
з і PreparedStatement
т.д.! Але коли ви передаєте його навколо, використовуйте 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
може бути призначений a, 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
задану 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 . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - JSR 310 .
Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти до класів java.time .
Ви можете обмінюватися об'єктами java.time безпосередньо зі своєю базою даних. Використовуйте драйвер JDBC, сумісний з JDBC 4.2 або пізнішої версії. Немає необхідності в струнах, немає потреби в java.sql.*
заняттях.
Де отримати класи java.time?
Клас 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());
}
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
якщо викликаються як очевидні з деталей їхньої реалізації.