最新的 Spring Data/Hibernate 在本机查询中不支持 java.time.LocalDate?

Posted

技术标签:

【中文标题】最新的 Spring Data/Hibernate 在本机查询中不支持 java.time.LocalDate?【英文标题】:java.time.LocalDate not supported in native queries by latest Spring Data/Hibernate? 【发布时间】:2018-04-05 19:33:00 【问题描述】:

问题:带有 Spring Data 返回日期的本机查询返回 java.sql.Date 而不是 java.time.LocalDate,尽管进行了设置。

上下文:一个带有 Spring Boot 2.0.0.M5(最新)、Hibernate 5.2.11、Hibernate-Java8 5.2.12(只要它在类路径上就支持 JSR310 类)的新项目。

下面的匿名示例(该应用与生日无关):

public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> 
    @Query(value = "select day from birthdays", nativeQuery = true)
    Iterable<java.sql.Date> getBirthdays(); //the return type should ideally be java.time.LocalDate

在数据库(SQL Server)中,day 字段为 DATE,值类似于 2017-10-24。

问题是在运行时,Spring Data 存储库(我无法控制其实现,或者有办法吗?)返回 java.sql.Date 而不是 java.time.LocalDate (澄清:返回类型似乎是由 Spring Data 决定的,即使我将返回类型更改为 java.time.LocalDate,也仍然是 java.sql.Date,这是我开始的方式。

没有办法直接得到LocalDate吗?我可以稍后转换它,但是(1)效率低下,(2)存储库方法必须返回旧的日期/时间类,这是我想避免的。我阅读了 Spring Data documentation,但没有任何内容。

编辑:对于任何有同样问题的人,下面是解决方案,Jens 建议的转换器。

public class LocalDateTypeConverter 

    @Converter(autoApply = true)
    public static class LocalDateConverter implements AttributeConverter<LocalDate, Date> 

        @Nullable
        @Override
        public Date convertToDatabaseColumn(LocalDate date) 
            return date == null ? null : new Date(LocalDateToDateConverter.INSTANCE.convert(date).getTime());
        

        @Nullable
        @Override
        public LocalDate convertToEntityAttribute(Date date) 
            return date == null ? null : DateToLocalDateConverter.INSTANCE.convert(date);
        
    

【问题讨论】:

我很困惑。你在你的界面中指定了你想要的Iterable&lt;java.sql.Date&gt; 为什么你还期待别的?为什么“存储库方法必须返回旧的日期/时间类?” 这就是 Spring Data 返回的内容。如果我输入 LocalDate (我最初这样做),当我从迭代器中检索项目时,它会在运行时失败并出现 ClassCastException。这就是为什么我发表评论的原因,类型应该是。我将补充说明。 【参考方案1】:

我知道这是一个较老的问题,但我对这个问题的解决方案不需要自定义转换器。

    public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> 
      @Query(value = "select cast(day as date) from birthdays", nativeQuery = true)
      Iterable<java.time.LocalDate> getBirthdays();
    

CAST 告诉 JPQL 使用可用的 java date\time 类型而不是 java.sql.Date

【讨论】:

酷!效果很好!【参考方案2】:

您似乎在转换器中发现了一个缺口。 Spring Data 在java.util.Datejava.time.LocalDate 之间进行开箱即用的转换,但不在java.time.LocalDatejava.sql.Date 以及java.sql 包中的其他日期和时间相关类型之间进行转换。

您可以创建自己的转换器来做到这一点。您可以使用Jsr310JpaConverters 作为模板。

此外,您可能想要创建一个功能请求,如果您构建一个转换器供您使用,您甚至可以提交一个拉取请求。

【讨论】:

谢谢,珍。如果知道的话,您能否澄清一下为什么 Spring Data(或者它是驱动程序)选择java.sql.Date 而不是java.util.Datejava.time.Date?数据库中的字段类型是日期(对于 SQL Server,就是它所说的,只是 YYYY-MM-DD)。在 3 种可能性中,如何选择特定的一种?另外,鉴于查询是本机的,如何告诉 Spring Data 使用它作为返回类型?没有什么要注释的,就像 JPA 实体和列的情况一样。谢谢。 无需查看代码的任何细节:Spring Data 可能只是将驱动程序提供的任何内容作为默认值,然后尝试使用其转换器将其转换为所需的类型。但在这种情况下,没有合适的转换器。 我怀疑转换器匹配逻辑着眼于完全相同的类,而不是子类。 java.sql.Datejava.util.Date 的子类(一个有用的讨论 here 所以后者的转换器应该/可以激活,但它没有。然后我会处理转换器。 转换器工作正常,所以我会完全接受答案。

以上是关于最新的 Spring Data/Hibernate 在本机查询中不支持 java.time.LocalDate?的主要内容,如果未能解决你的问题,请参考以下文章

JPA---Spring-data-JPA---Hibernate

Spring Data - Hibernate的配置

Java SE + Spring Data + Hibernate

优化 Spring-data / Hibernate ManyToMany 插入

Spring-data-jpa + Hibernate 未创建预期表

Spring Data + Hibernate 5 (Spring Boot 1.4) - 获取弃用消息