JPA2.1:引入Java8日期/时间API
我想在启用JPA的应用程序中为Java 8日期/时间API(JSR-310)添加支持。JPA2.1:引入Java8日期/时间API,java,hibernate,jpa,eclipselink,jpa-2.1,Java,Hibernate,Jpa,Eclipselink,Jpa 2.1,我想在启用JPA的应用程序中为Java 8日期/时间API(JSR-310)添加支持。 很明显,JPA2.1 作为一种解决方法,最常见的建议是使用 在我现有的应用程序中,我将实体更改为对列映射字段使用LocalDate/LocalDateTime类型,并为它们添加了java.util.Date的旧setter/getter。 我创建了相应的AttributeConverter类 我的应用程序现在在使用Query.setParameter()和java.util.Date实例时失败了(它在转换到新
很明显,JPA2.1
作为一种解决方法,最常见的建议是使用 在我现有的应用程序中,我将实体更改为对列映射字段使用
LocalDate
/LocalDateTime
类型,并为它们添加了java.util.Date
的旧setter/getter。我创建了相应的
AttributeConverter
类
我的应用程序现在在使用Query.setParameter()
和java.util.Date
实例时失败了(它在转换到新API之前工作)JPA似乎期待新的日期类型,而不是动态转换。
我希望,如果将一个参数传递给注册了
AttributeConverter
的类型的setParameter()
,它将被转换器自动转换。
但情况似乎并非如此,至少不使用EclipseLink 2.6.2
:
java.lang.IllegalArgumentException: You have attempted to set a value of type class java.util.Date for parameter closeDate with expected type of class java.time.LocalDate from query string SELECT obj FROM [...]
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:937) ~[eclipselink-2.6.2.jar:2.6.2.v20151217-774c696]
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:593) ~[eclipselink-2.6.2.jar:2.6.2.v20151217-774c696]
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:1) ~[eclipselink-2.6.2.jar:2.6.2.v20151217-774c696]
[...]
问题:
更新:
但是,似乎至少在使用EclipseLink时,
属性转换器
存在的自定义类型并没有得到完全支持:在JPQL查询中,实际字段类型和转换的数据库类型都不能用作参数。 使用转换后的数据库类型时,会发生上述异常。 当使用实际字段类型(例如,
LocalDate
)时,它直接传递给不知道此类型的jdbc驱动程序:
Caused by: java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:10495)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:9974)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:10799)
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:10776)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:241)
at org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.setParameterValueInDatabaseCall(DatabasePlatform.java:2506)
我希望EclipseLink使用AttributeConverter将字段类型转换为java.sql类型。
(另请参见此错误报告:)
这就引出了最重要的问题#4:
EclipseLink
,的java 8日期字段,包括在此类字段上使用查询参数的可能性Java.util.Date
替换为LocalDate
和LocalDateTime
AttributeConverter
仅在用于实体字段的类型和数据库类型之间转换(即java.sql.Date
等);它不会在实体字段类型和查询参数中使用的java.util.Date
之间转换java.time
类型引入JPA实体之后,没有办法在现有代码中继续使用java.util.Date
AttributeConverter
实现之外,我还将java.util.Date
的所有出现更改为适当的java.time
类型,不仅在实体中,而且在JPA-QL查询和业务方法中java.util
和java.time
之间进行转换来实现,但它不会一直实现。更重要的是,如果您不愿意转换使用这些属性的剩余代码,我不太明白将java.time
类型引入JPA实体属性的意义
属性。在我在那个JavaEE应用程序中完成转换工作后,
Java.util.Date
的用途就没有了(尽管我还必须为JSF创建转换器)。AttributeConverter按设计工作,因为转换器是用来处理实体类型和数据库类型之间的来回转换。验证是验证参数的类型是否与实体中的类型不匹配-仅仅因为属性转换器可以处理它,并不意味着它符合属性转换器类型的约定。JPA只说它将在进入数据库之前通过转换器,在这种情况下它不会进入数据库
如果你不喜欢罗盖里奥的建议,你可以
1) 修改EclipseLink代码以放松验证,使其能够通过转换器,或者
2) 改为将属性类型更改为“Object”,以便您可能传入的所有参数类型都将进入转换器。由于提供程序本身存在许多错误,我认为您除了在映射级别使用
java.util.Date
和在API级别使用java 8日期之外别无选择
假设您为名为DateUtils
的实用程序编写了一个类,您可以按如下方式定义映射:
@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);
}
}
然后在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();
}
公共列表读取日期大于(LocalDateTime日期){
Query Query=em.createQuery(“从MyEntity e中选择e,其中e.date>:date”);
query.setParameter(“日期”,dateUils.convertToDate(日期));
返回query.getResultList();
}
因此,
java.util
日期将在实体和DAO(存储库)内部使用,而实体和DAO公开的API将获取/返回java 8日期,从而使应用程序的其余部分只能使用java 8日期操作。我有以下设置:
- EclipseLink v2.6.2
- h2数据库v1.4.191
- 爪哇8
@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;
}
}
JPA 2.1所需的转换器为:
@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();
}
}
@转换器(autoApply=true)
公共类LocalDateTimeConverter实现AttributeConverter{
@凌驾
公众的
List<?> result = em.createQuery(
"SELECT p FROM MeasuringPoint p WHERE p.when = :custDate")
.setParameter("custDate", LocalDateTime.now())
.getResultList();