Buy me a coffee »

Joda-Time with Java EE and JPA 2

With Java 8 there is a new date and time structure which was inspired from Joda-Time. It is far more better than the old and clumsy Date and Calendar constructs. Nevertheless, if you want to store the time in a database, you have to tinker a bit because currently there is no supported out-of-the-box solution.

In this article I present solutions how to persist Joda-Time constructs into your database with JPA 2 (JPA 2.0 and JPA 2.1).

JPA 2.0

There was a time long before Java 8 and improved Date and Time constructs. And then you had only Date and Calendar objects until Joda-Time came and better software projects switched to Joda-Time because you could handle time better (and had nano seconds to calculate with but this is not an everyday need in enterprise projects).

And if your company is using still Java EE 6 (with either Java 6 or Java 7) and JPA 2.0 you need a method to persist Joda-Time constructs.

As I told in the introduction, there is no out-of-the-box solution for this. So my solution will involve Hibernate as the JPA provider.

What you will need, is the Jadira Framework which provides user types for Hibernate — and among these user types there are some for Joda-Time objects.

The framework is well documented so I will not go into detail how to declare each construct with Hibernate. Instead I’ll show only one example: how can you persist a LocalDate object.

@Column
@org.hibernate.annotations.Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private LocalDate dateToPersist;

And this was it. If you want to persist another type of Joda-Time construct, you’ll find the corresponding type in the Jadira Framework.

JPA 2.1

And with Java EE 7 came JPA 2.1 along. OK, JPA 2.1 does not support the new Java 8 time constructs out of the box either but (and this is a significant one) you have AttributeConverters which help you persisting the new time constructs.

The usage of AttributeConverter is not limited to the new Java 8 time constructs — you can use it with any types you want to persist and there is no out-of-the-box solution for it in the JPA 2.1 world.

Adam Bien mentioned a way how to use the AttributeConverter, but in the first version it was a bit buggy because it did not handle Instant objects the right way — and I get some exceptions when I tried to implement it. So I tweaked a bit with the code and the result is the following:

@Converter(autoApply = true)
public class LocalDateConverter implements AttributeConverter<LocalDate, Date> {
    @Override
    public Date convertToDatabaseColumn(LocalDate date) {
    if (date == null) {
        return null;
    }

    final Instant instant = date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }

    @Override
    public LocalDate convertToEntityAttribute(Date value) {
    if (value == null) {
        return null;
    }

    final Instant instant = Instant.ofEpochMilli(value.getTime());
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
    }
}

The autoApply = true makes your life easier so you do not have to explicitly tell the JPA provider what converter it has to use when dealing with a LocalDate object. You can simply write in your entity:

@Entity
public class ExampleEntity {
    @Id
    @GeneratedValue
    private Long id;
    private LocalDate created;
}

Source code

I prepared an example application for JPA 2.1 at my GitHub account. Feel free to use it.

If you have any questions feel free to ask them.

About the author

GHajba

Senior developer, consultant, author, mentor, apprentice. I love to share my knowledge and insights what I achieve through my daily work which is not trivial -- at least not for me.

Leave a comment: