JPA 2.1: Introducing Java 8 Date/Time API

时间秒杀一切 提交于 2019-12-05 01:46:24

Some time ago, I converted a Java EE 7 web app from Java 7 to Java 8, and replaced java.util.Date in entities with LocalDate and LocalDateTime.

  1. Yes, that behavior is expected, because an AttributeConverter only converts between the type used for entity fields and the database type (ie, java.sql.Date, etc.); it does not convert between an entity field type and a java.util.Date used in a query parameter.
  2. As far as I know, no, there is no way to continue using java.util.Date in existing code, after introducing the java.time types into JPA entities.
  3. Besides creating the necessary AttributeConverter implementations, I changed all occurrences of java.util.Date to the appropriate java.time types, not only in entities but also in JPA-QL queries and in business methods.

For item 2, of course you can go some way by using utility methods and getters/setters that convert between java.util and java.time, but it's not going to go all the way. More importantly, I don't quite see the point of introducing java.time types into JPA entity attributes, if you are not willing to convert the remaining code that uses these attributes. After the conversion work I did in that Java EE app, there were no uses of java.util.Date left anywhere (though I also had to create converters for JSF).

With so many bugs in the provider itself I don't think you have much choice but to use java.util.Date on the mapping level and java 8 dates on the API level.

Assuming you write a utility class for conversion to/from java.util dates called DateUtils, you could define your mappings as follows:

@Entity
public class MyEntity {

  @Column("DATE")
  private Date date; // java.util.Date

  public void setDate(LocalDateTime date) {
    this.date = DateUtils.convertToDate(date);
  }

  public LocalDateTime getDate() {
    return DateUtils.convertFromDate(date);
  }
}

Then to filter by date in JPQL:

public List<MyEntity> readByDateGreaterThan(LocalDateTime date) {
  Query query = em.createQuery("select e from MyEntity e where e.date > :date");
  query.setParameter("date", DateTuils.convertToDate(date));
  return query.getResultList();
}

So, java.util dates would be used in entities and DAOs (Repositories) internally, while the API exposed by entities and DAOs would take/return java 8 dates, thus enabling the rest of the application to operate with java 8 dates only.

Steffen Harbich

I have the following setup:

  • EclipseLink v2.6.2
  • h2 Database v1.4.191
  • Java 8

The entity class is like:

@Entity
public class MeasuringPoint extends BaseEntity {

    @Column(nullable = false)
    private LocalDateTime when;

    public void setWhen(LocalDateTime when) {
        this.when = when;
    }

    public LocalDateTime getWhen() {
        return when;
    }

}

The required converter for JPA 2.1 is:

@Converter(autoApply = true)
public class LocalDateTimeConverter implements AttributeConverter<LocalDateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime attribute) {
        return attribute == null ? null : Timestamp.valueOf(attribute);
    }

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp dbData) {
        return dbData == null ? null : dbData.toLocalDateTime();
    }

}

Now I can make a query

List<?> result = em.createQuery(
         "SELECT p FROM MeasuringPoint p WHERE p.when = :custDate")
         .setParameter("custDate", LocalDateTime.now())
         .getResultList();

and it works like a charm. The result contains the expected entities. The conversion to TIMESTAMP is done automatically. When you have queries using the Criteria API, have a look at this answer which shows how to use LocalDateTime within Criteria API queries.

I wonder why it doesn't work with your code. Maybe the H2 JDBC driver does support something your Oracle doesn't.

AttributeConverter is working as designed, as the converter is meant to handle the back and forth between your entity type and the database type. Validation is verifying the type of the parameter doesn't match the type within the entity - just because your attribute converter can handle it, doesn't mean it fits the contract of the attribute converter types. JPA only says it will go through the converter before going to the database, and in this case it doesn't go to the database.

If you don't like Rogério's suggestions, you can 1) modify the EclipseLink code to relax the validation to allow it to go through to your converter, or 2) change your attribute type to 'Object' instead so that all parameter types you may pass in will go to your converter.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!