Хибернация - картографиране на дата и час

1. Въведение

В тази статия ще покажем как да картографираме стойностите на временните колони в Hibernate, включително класовете от пакетите java.sql , java.util и java.time .

2. Настройка на проекта

За да демонстрираме картографирането на временните типове, ще ни е необходима базата данни H2 и най-новата версия на библиотеката с хибернация-ядро :

 org.hibernate hibernate-core 5.4.12.Final   com.h2database h2 1.4.194 

За текущата версия на hibernate-core библиотеката се насочете към хранилището Maven Central.

3. Настройка на часовата зона

Когато се занимавате с дати, е добра идея да зададете конкретна часова зона за JDBC драйвер. По този начин приложението ни ще бъде независимо от текущата часова зона на системата.

За нашия пример ще го настроим на база сесия:

session = HibernateUtil.getSessionFactory().withOptions() .jdbcTimeZone(TimeZone.getTimeZone("UTC")) .openSession();

Друг начин би бил да настроите свойството hibernate.jdbc.time_zone във файла със свойства на Hibernate, който се използва за изграждане на фабричната сесия. По този начин можем да посочим часовата зона веднъж за цялото приложение.

4. Картографиране на java.sql типове

Пакетът java.sql съдържа JDBC типове, които са подравнени с типовете, дефинирани от SQL стандарта:

  • Дата съответства на типа DATE SQL, който е само дата без време
  • Времето съответства на типа TIME SQL, който е час от деня, посочен в часове, минути и секунди
  • Клеймото за време включва информация за дата и час с точност до наносекунди и съответства на типа TIMESTAMP SQL

Тъй като тези типове са в съответствие с SQL, така и тяхното картографиране е относително директно. Можем да използваме или анотацията @Basic, или @Column :

@Entity public class TemporalValues { @Basic private java.sql.Date sqlDate; @Basic private java.sql.Time sqlTime; @Basic private java.sql.Timestamp sqlTimestamp; }

След това бихме могли да зададем съответните стойности по следния начин:

temporalValues.setSqlDate(java.sql.Date.valueOf("2017-11-15")); temporalValues.setSqlTime(java.sql.Time.valueOf("15:30:14")); temporalValues.setSqlTimestamp( java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));

Имайте предвид, че изборът на типове java.sql за полета на обектите не винаги може да бъде добър избор. Тези класове са специфични за JDBC и съдържат много отхвърлена функционалност.

5. Картографиране на java.util.Date Type

Типът java.util.Date съдържа информация за дата и час до точност до милисекунди. Но това не е пряко свързано с който и да е тип SQL.

Ето защо се нуждаем от друга анотация, за да посочим желания тип SQL:

@Basic @Temporal(TemporalType.DATE) private java.util.Date utilDate; @Basic @Temporal(TemporalType.TIME) private java.util.Date utilTime; @Basic @Temporal(TemporalType.TIMESTAMP) private java.util.Date utilTimestamp;

В @Temporal анотацията има единствен параметър стойност на тип TemporalType. Може да бъде DATE , TIME или TIMESTAMP , в зависимост от основния тип SQL, който искаме да използваме за картографиране.

След това бихме могли да зададем съответните полета по следния начин:

temporalValues.setUtilDate( new SimpleDateFormat("yyyy-MM-dd").parse("2017-11-15")); temporalValues.setUtilTime( new SimpleDateFormat("HH:mm:ss").parse("15:30:14")); temporalValues.setUtilTimestamp( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") .parse("2017-11-15 15:30:14.332"));

Както видяхме, на java.util.Date тип (милисекунди прецизност) не е достатъчно точно, за да се справят с клеймо стойност (наносекунди прецизност) на.

Така че, когато извличаме обекта от базата данни, не е изненадващо да намерим екземпляр java.sql.Timestamp в това поле, дори ако първоначално сме запазили java.util.Date :

temporalValues = session.get(TemporalValues.class, temporalValues.getId()); assertThat(temporalValues.getUtilTimestamp()) .isEqualTo(java.sql.Timestamp.valueOf("2017-11-15 15:30:14.332"));

Това трябва да е добре за нашия код, тъй като Timestamp удължава Date .

6. Картиране на java.util.Calendar Type

Както при java.util.Date , типът java.util.Calendar може да бъде преобразуван в различни типове SQL, така че трябва да ги посочим с @Temporal .

Единствената разлика е, че Hibernate не поддържа картографиране на Календар в TIME :

@Basic @Temporal(TemporalType.DATE) private java.util.Calendar calendarDate; @Basic @Temporal(TemporalType.TIMESTAMP) private java.util.Calendar calendarTimestamp;

Ето как можем да зададем стойността на полето:

Calendar calendarDate = Calendar.getInstance( TimeZone.getTimeZone("UTC")); calendarDate.set(Calendar.YEAR, 2017); calendarDate.set(Calendar.MONTH, 10); calendarDate.set(Calendar.DAY_OF_MONTH, 15); temporalValues.setCalendarDate(calendarDate);

7. Картографиране на java.time Видове

От Java 8, новият API за дата и час на Java е достъпен за работа с временни стойности . Този API решава много от проблемите на класовете java.util.Date и java.util.Calendar .

Типовете от пакета java.time се свързват директно към съответните типове SQL. Така че няма нужда да посочвате изрично @Temporal анотация:

  • LocalDate се преобразува в DATE
  • LocalTime и OffsetTime се преобразуват в TIME
  • Instant , LocalDateTime , OffsetDateTime и ZonedDateTime се преобразуват в TIMESTAMP

Това означава, че можем да маркираме тези полета само с @Basic (или @Column ) анотация, като тази:

@Basic private java.time.LocalDate localDate; @Basic private java.time.LocalTime localTime; @Basic private java.time.OffsetTime offsetTime; @Basic private java.time.Instant instant; @Basic private java.time.LocalDateTime localDateTime; @Basic private java.time.OffsetDateTime offsetDateTime; @Basic private java.time.ZonedDateTime zonedDateTime;

Всеки времеви клас в пакета java.time има статичен метод parse () , за да анализира предоставената стойност на String, използвайки подходящия формат. Ето как можем да зададем стойностите на полетата на обекта:

temporalValues.setLocalDate(LocalDate.parse("2017-11-15")); temporalValues.setLocalTime(LocalTime.parse("15:30:18")); temporalValues.setOffsetTime(OffsetTime.parse("08:22:12+01:00")); temporalValues.setInstant(Instant.parse("2017-11-15T08:22:12Z")); temporalValues.setLocalDateTime( LocalDateTime.parse("2017-11-15T08:22:12")); temporalValues.setOffsetDateTime( OffsetDateTime.parse("2017-11-15T08:22:12+01:00")); temporalValues.setZonedDateTime( ZonedDateTime.parse("2017-11-15T08:22:12+01:00[Europe/Paris]"));

8. Заключение

В тази статия показахме как да картографираме временни стойности от различни типове в хибернация.

Изходният код на статията е достъпен в GitHub.