Статьи

Использование Java 8 Date-Time API с JSF и Java EE 7

Если вы используете Java 8 с Java EE 7, то при попытке использовать некоторые из новых функций Java 8 могут возникать некоторые причуды. Одна из таких странностей заключается в том, что новый API Date-Time по умолчанию не работает со многими API Java EE 7, поскольку они созданы для работы с java.util.Date и / или более старыми API Date. Однако это не препятствие, так как есть много способов обойти такие проблемы. В этой статье я покажу, как можно настроить приложение JSF, чтобы разрешить использование API-интерфейсов даты и времени Java 8 вместе с JPA и преобразователями даты.

Прежде всего, если вы хотите сохранить даты, используя новый класс LocalDate (или другие, которые являются частью Java 8 Date-Time API), вам необходимо разработать конвертер, который будет автоматически конвертировать из java.time.LocalDate в java.util.Date и наоборот для работы с JPA 2.1. Это достаточно легко сделать, тем более что нет необходимости настраивать какой-либо XML для установки конвертера. Следующий код является конвертером, который используется для обеспечения поддержки даты и времени Java 8 для JPA:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.util.Date;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
 
/**
 * Converter to provide Java 8 Date/Time API Support to JPA
 *
 * @author Juneau
 */
@Converter(autoApply = true)
public class LocalDatePersistenceConverter implements AttributeConverter<LocalDate, Date> {
    @Override
    public Date convertToDatabaseColumn(LocalDate entityValue) {
        LocalTime time = LocalTime.now();
        Instant instant = time.atDate(entityValue).atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }
 
    @Override
    public LocalDate convertToEntityAttribute(Date databaseValue) {
        Instant instant = Instant.ofEpochMilli(databaseValue.getTime());
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
    }
}

Глядя на код, метод convertToDatabaseColumn() принимает
LocalDate из объекта, класса, а затем использует некоторые из утилит Java 8 Date-Time API для преобразования его в java.util.Date чтобы он мог быть сохранен в базе данных. Второй метод convertToEntityAttribute() берет java.util.Date из JPA и преобразует его в обратном направлении в объект LocalDate для использования с приложением на основе Java 8. Аннотация @Converter регистрирует класс как преобразователь, а реализация AttributeConverter применяет преобразователь к классу сущностей, чтобы преобразовать состояние в столбец базы данных и обратно.

Затем, если вы попытаетесь применить JSF-конвертер к Java 8 LocalDate в вашем приложении, скажем, в представлении, у вас возникнут проблемы, если вы не напишите специальную реализацию FacesConverter для применения к компоненту, который вы хотите преобразовать в LocalDate . Написание FacesConverter так же просто, как преобразователя атрибутов класса сущности, а регистрация так же проста, как применение аннотации к преобразователю. Следующий класс является примером FacesConverter , который преобразует java.time.LocalDate в java.util.Date для использования в компоненте JSF.

Примечание. Это также работает с популярными библиотеками компонентов JSF, такими как PrimeFaces.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.FacesConverter;
 
/**
 * Faces converter for support of LocalDate
 * @author Juneau
 */
@FacesConverter(value="localDateTimeConverter")
public class LocalDateTimeConverter implements javax.faces.convert.Converter {
 
    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
          return LocalDate.parse(value);
    }
 
    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
 
        LocalDate dateValue = (LocalDate) value;
         
        return dateValue.format(DateTimeFormatter.ofPattern("MM/dd/yyyy"));
    }
     
}

Теперь давайте посмотрим на код немного. Этот класс FacesConverter зарегистрирован через аннотацию @FacesConverter , и класс может просто реализовать интерфейс javax.faces.convert.Converter . Далее взгляните на реализацию. Метод getAsObject() используется для анализа строки в компоненте и ее возврата в виде java.time.LocalDate , тогда как метод getAsString() принимает объект LocalDate и возвращает его в виде строки в указанном формате даты. Это демонстрирует еще одну приятную особенность Java 8 …
Класс DateTimeFormatter, который облегчает форматирование объекта java.time.* .

Вот и все … не так уж сложно использовать красивый API Date-Time Java 8 в приложении Java EE 7. Теперь давайте применим конвертер к компоненту даты. Следующая разметка демонстрирует, как применить конвертер к календарному компоненту PrimeFaces.

1
2
3
4
5
<p:calendar id="enterDate" converter="localDateTimeConverter" style="width: 100%;"
 
  readonly="true" value="#{myExcellentJsfController.current.enterDate}">
 
  </p:calendar>