terça-feira, 22 de novembro de 2016

Using LocalDate in a JEE7 application

So I was in a Java User Group (GUJavaSC) Meetup's talk about the use of the new Java features. When Rodrigo Cândido (the speaker) mentioned the new Java Date/Time, one of my CIASC (the company I'm working on) colleagues asked:"What about ORM? Can I use LocalDate in a JPA mapping?".

The answer was, as I expected, yes, it can be used.
That's when it hit me: I'm working on a small project, and I'm using a JBoss Forge generated, pure JEE7 application with JPA, and I'm using java.util.Date on my entities. I had to fix that.

I thought it should be easy. Just replacing java.util.Date with the new java.time.LocalDate. Right?

Nope. It turns out that my JAX-RS REST endpoints didn't like the new features. Jackson couldn't decode/encode that right out of the box.

The same happened in a Spring-boot app I'm working on.

In this post I'll show what had to be done to make it work.

Part 1: Changing the O/R Mapping

Expense.entity (DateTimeFormat is a Spring thing...)
...
@Entity@Table(name = "expenses")
public class Expense {

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    private LocalDate date;

    @NotNull
    @DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
    private LocalTime time;
 ...

Part 2: Importing the necessary library

pom.xml
...
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.8.4</version>
</dependency>
...

Part 3: Registering the Jackson Module with a @Provider

Create a new class JacksonJavaTimeRegistryProvider (or any other name you like)
...
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

/**
* Provider for enabling java.time classes to be parsed to/from
* strings in the REST requests. 
* Uses the Jackson JSR310 enabler
* lib. 
*/ 
@Provider
public class JacksonJavaTimeRegistryProvider implements ContextResolver<ObjectMapper> {

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return new ObjectMapper().registerModule(new JavaTimeModule());
    }

}

...and that's it.