First things first, if you wish to persist dates using the new LocalDate class (or others that are part of the Java 8 Date-Time API), you need to develop a converter which will automatically convert from java.time.LocalDate to java.util.Date and vice versa in order to work with JPA 2.1. This is easy enough to do, especially since there is no need to configure any XML to establish the converter. The following code is a converter that is used to provide Java 8 Date-Time support for JPA:
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();
}
}
Looking at the code, the convertToDatabaseColumn() method accepts a LocalDate from the entity, class and then utilizes some of the Java 8 Date-Time API utilities to convert it to a java.util.Date so that it can be stored into the database. The second method, convertToEntityAttribute() takes a java.util.Date from JPA and converts it in the opposite direction into a LocalDate object for use with your Java 8 based application. The @Converter annotation registers the class as a converter, and implementing AttributeConverter applies the converter to an entity class in order to convert the state to a database column and back again.
Next, if you attempt to apply a JSF converter to a Java 8 LocalDate within your application, say within a view, you will experience issues unless you write a special FacesConverter implementation to apply against the component that you wish to convert to the LocalDate. Writing a FacesConverter is just as simple as the entity class attribute converter, and registration is as easy as applying an annotation to the converter. The following class is an example of the FacesConverter that will convert a java.time.LocalDate to a java.util.Date for use within a JSF component. Note: This also works with popular JSF component libraries, such as PrimeFaces.
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"));
}
}
Now let's look at the code a bit. This FacesConverter class is registered via the @FacesConverter annotation, and the class can simply implement the javax.faces.convert.Converter interface. Next, take a look at the implementation. The getAsObject() method is used to parse a String from the component and return it as a java.time.LocalDate, whereas the getAsString() method accepts a LocalDate object and returns it as a String in the specified date format. This demonstrates another nice feature of Java 8...the DateTimeFormatter class, which makes it easy to format a java.time.* object.
That's it...not too difficult to use the nice Java 8 Date-Time API within a Java EE 7 application. Now let's apply the converter to a date component. The following markup demonstrates how to apply the converter to a PrimeFaces calendar component.
<p:calendar id="enterDate" converter="localDateTimeConverter" style="width: 100%;"
readonly="true" value="#{myExcellentJsfController.current.enterDate}">
</p:calendar>