Рубрики
Без рубрики

Что нового в JPA 2.2 – Типы даты и времени Java 8

Добавление даты и времени JPA 2.2 позволяет использовать объекты даты и времени Java 1.8, расположенные в пакете java.time, в качестве основных типов в сопоставлениях сущностей.

Автор оригинала: Vlad Mihalcea.

Теперь, когда Бюллетень для обзора JPA 2.2 был одобрен давайте начнем анализировать некоторые новые дополнения к стандарту, которые уже довольно давно поддерживаются Hibernate. В этой статье мы рассмотрим, как работает JPA 2.2 Дата/время и какие типы необходимо использовать в зависимости от требований вашего бизнес-кейса.

В журнале изменений JPA 2.2 указано, что будут поддерживаться только следующие типы :

  • В журнале изменений
  • JPA 2.2
  • указано, что будут поддерживаться только
  • следующие типы
  • :

В то время как LocalDateTime довольно прост, поскольку он фиксирует только дату и время, OffsetDateTime более проблематичен, поскольку он фиксирует только смещение, но не правила часового пояса, такие как летнее время (летнее время) или другие правила, определенные ZoneId и обычно поддерживаемые ZonedDateTime .

Также любопытно, что стандарт не поддерживает java.time.Продолжительность тип, который может пригодиться во многих случаях использования в бизнесе.

Учитывая, что в нашей системе есть следующие сущности:

Сущность Сотрудник содержит следующие атрибуты даты и времени Java 8:

  • день рождения атрибут является Локальной датой , так как нас интересует только часть данных
  • обновлено является LocalDateTime , так как этот атрибут должен хранить информацию как о дате, так и о времени
@Entity(name = "Employee")
public class Employee {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String name;

    private LocalDate birthday;

    @Column(name = "updated_on")
    private LocalDateTime updatedOn;

    //Getters are setters omitted for brevity
}

Для получения более подробной информации об аннотации @Natural ознакомьтесь с этой статьей .

Сущность Собрание содержит следующие атрибуты даты и времени Java 8:

  • начинается с – это Zoneddatetime , который, даже если не поддерживается JPA 2.2, вероятно, является более подходящей версией OffsetDateTime
  • атрибут длительность может не поддерживаться JPA 2.2, но мы будем использовать его, так как Hibernate поддерживает все эти типы
@Entity(name = "Meeting")
public class Meeting {

    @Id
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "employee_id")
    private Employee createdBy;

    @Column(name = "starts_at")
    private ZonedDateTime startsAt;

    private Duration duration;

    //Getters are setters omitted for brevity
}

Предполагая, что мы сохраняем следующие сущности:

Employee employee = new Employee();
employee.setName( "Vlad Mihalcea" );
employee.setBirthday(
    LocalDate.of(
        1981, 12, 10
    )
);
employee.setUpdatedOn(
    LocalDateTime.of(
        2015, 12, 1,
        8, 0, 0
    )
);

entityManager.persist( employee );

Meeting meeting = new Meeting();
meeting.setId( 1L );
meeting.setCreatedBy( employee );
meeting.setStartsAt(
    ZonedDateTime.of(
        2017, 6, 25,
        11, 30, 0, 0,
        ZoneId.systemDefault()
    )
);
meeting.setDuration(
    Duration.of( 45, ChronoUnit.MINUTES )
);

entityManager.persist( meeting );

Hibernate создаст следующие инструкции SQL:

INSERT INTO Employee (
    birthday, 
    name, 
    updated_on, 
    id
) 
VALUES (
    '1981-12-10', 
    'Vlad Mihalcea', 
    '2015-12-01 08:00:00.0', 
    1
)

INSERT INTO Meeting (
    employee_id, 
    duration, 
    starts_at, 
    id
) 
VALUES (
    1, 
    2700000000000, 
    '2017-06-25 11:30:00.0', 
    1
)

Оба LocalDateTime и ZonedDateTime совместно используют org.hibernate.type.descriptor.sql.TimestampTypeDescriptor , что означает, что они будут сохранены как java.sql.Метка времени .

Хотя в LocalDateTime отсутствует информация о часовом поясе, ZonedDateTime потеряет информацию о часовом поясе при сохранении в соответствующем столбце базы данных.

При загрузке обратно наших объектов:

Employee employee = entityManager
.unwrap( Session.class )
.bySimpleNaturalId( Employee.class )
.load( "Vlad Mihalcea" );

assertEquals(
    LocalDate.of(
        1981, 12, 10
    ),
    employee.getBirthday()
);
assertEquals(
    LocalDateTime.of(
        2015, 12, 1,
        8, 0, 0
    ),
    employee.getUpdatedOn()
);

Meeting meeting = entityManager.find( Meeting.class, 1L );
assertSame(
    employee, meeting.getCreatedBy()
);
assertEquals(
    ZonedDateTime.of(
        2017, 6, 25,
        11, 30, 0, 0,
        ZoneId.systemDefault()
    ),
    meeting.getStartsAt()
);
assertEquals(
    Duration.of( 45, ChronoUnit.MINUTES ),
    meeting.getDuration()
);

Исходные объекты времени данных Java 8 соответствуют тем, которые сохраняются.

Причина, по которой ZonedDateTime совпадает с ранее сохраненным, заключается в том, что базовое значение МЕТКИ ВРЕМЕНИ было перенесено в текущий системный часовой пояс.

Пока все драйверы JDBC не будут поддерживать МЕТКУ ВРЕМЕНИ С ЧАСОВЫМ ПОЯСОМ на уровне привязки java.sql.Statement значения параметра , что является требованием для HHH-11773 , сомнительно, что вам действительно нужно использовать ZonedDateTime или OffsetDateTime .

В настоящее время гораздо разумнее сохранить все МЕТКА ВРЕМЕНИ значения в UTC , что означает, что LocalDateTime гораздо лучше подходит для ваших объектов JPA.