Utilizing the Java 8 Date-Time API with JSF and Java EE 7
If you are using Java 8 with Java EE 7, then there may be some quirks that you run into when trying to utilize some of the Java 8 new features. One such quirk is that the new Date-Time API does not work with many of the Java EE 7 APIs by default since they are built to work with java.util.Date and/or the older Date APIs. However, this is not a road block, as there are many ways to work around such issues. In this post, I will demonstrate how you can tweak your JSF application to allow use of the Java 8 Date-Time APIs along with JPA and date converters.
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>
Reference: | Utilizing the Java 8 Date-Time API with JSF and Java EE 7 from our JCG partner Josh Juneau at the Josh’s Dev Blog – Java, Java EE, Jython, Oracle, and More… blog. |
Hi man, i got this error: Text ’08/08/16′ could not be parsed at index 0
Do you know why ?