【问题标题】:JPQL not working with datesJPQL 不适用于日期
【发布时间】:2017-12-31 04:24:35
【问题描述】:

我无法使用查询中的日期从数据库中获取数据...

我正在开发使用 Spring Data JPA 和 Oracle 数据库的 Web 应用程序。我在接口中使用了 @RepositoryRestResource 注释,我只是使用 @Param 声明了一些带有命名参数的查询方法和 @Query 注释。今天我需要添加一个带有日期的新实体。在数据库中,两列都是 DATE 类型,并且在查询中使用。但我也有另一种类型的 TIMESTAMP ,也许我将来需要使用。在这两个列的 Java 表示下方,当然还有所有 setter 和 getter,但它有更多字段,所以只需添加以下内容:

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "INIT_DATE")
private Calendar initDate;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "AGG_DATE")
private Calendar aggDate;

我还为案例创建了新界面,与往常一样:

@RepositoryRestResource(collectionResourceRel = "customer", path = "customer")
public interface ICustomerRepository extends PagingAndSortingRepository<Customer, Long> {

    @Query("SELECT c FROM Customer c where c.initDate <= TO_DATE(:currentDate, 'yyyy-MM-dd') AND c.aggDate >= TO_DATE(:currentDate, 'yyyy-MM-dd')")
    public List<Customer> filterByDate(@Param("currentDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Calendar currentDate);

}

我收到了这个错误:

org.springframework.dao.DataIntegrityViolationException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not extract ResultSet
ORA-01858: a non-numeric character was found where a numeric was expected

我正在尝试使用此 http 请求从数据库中获取此数据:

http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10

在 SQL Developer 中,查询工作正常。

我在某处读到 JPQL 中没有日期函数,但在日志中我可以看到如下所示的查询和参数:

select
    customer0_.customerId as col_0_0_,
    customer0_.customerName as col_0_1_,
    customer0_.aggDate as col_0_2_,
    customer0_.initDate as col_0_3_,
from
    customer customer0_ 
where       
    and customer0_.aggDate>=to_date(?, 'yyyy-MM-dd') 
    and customer0_.initDate<=to_date(?, 'yyyy-MM-dd')
2017-07-25 11:55:22.550 TRACE 12252 --- [ (self-tuning)'] o.h.type.descriptor.sql.BasicBinder      : binding parameter [8] as [TIMESTAMP] - [java.util.GregorianCalendar[time=1499637600000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Berlin,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=6,WEEK_OF_YEAR=28,WEEK_OF_MONTH=3,DAY_OF_MONTH=10,DAY_OF_YEAR=191,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=3600000,DST_OFFSET=3600000]]
2017-07-25 11:55:22.550 TRACE 12252 --- [ (self-tuning)'] o.h.type.descriptor.sql.BasicBinder      : binding parameter [9] as [TIMESTAMP] - [java.util.GregorianCalendar[time=1499637600000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Berlin,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=6,WEEK_OF_YEAR=28,WEEK_OF_MONTH=3,DAY_OF_MONTH=10,DAY_OF_YEAR=191,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=3600000,DST_OFFSET=3600000]]

老实说,我不知道这里有什么问题...数据库中的格式日期是 yy/MM/DD,但它也不适用于我...你能告诉我什么我错过了还是做错了??

编辑 [Gusti Arya 的回答]:

我尝试了两件事。在您更新之前,我只是更改了类型并离开了@DateTimeFormat。然后我遇到了与日历类型相同的错误。 删除@DateTimeFormat 后,更新后也一样,我收到此错误:

org.springframework.data.repository.support.QueryMethodParameterConversionException: Failed to convert 2017-07-10 into java.util.Date!
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [org.springframework.data.repository.query.Param java.util.Date] for value '2017-07-10'; nested exception is java.lang.IllegalArgumentException

更有趣的是,我有几乎相同的第二个查询,但没有 TO_DATE 函数,现在我遇到了与上述相同的错误。以前我有:

Persistent Entity Must not be null

EDIT 2 [与日志中的查询有关]:

我刚刚注意到我在此处发布的查询与我在日志中看到的不同...我的实体包含 EmbeddedId,其中是 customerId 和 customerName。这两列出现三次......所以这是查询的有效日志:

select
    customer0_.customerId as col_0_0_,
    customer0_.customerName as col_0_1_,
    customer0_.customerId as col_1_0_,
    customer0_.customerName as col_1_1_,
    customer0_.customerId as customerId1_6_,
    customer0_.customerName as customerName2_6_,
    customer0_.aggDate as aggDate3_6_,
    customer0_.initDate as initDate4_6_,
from
    customer customer0_ 
where       
    and customer0_.aggDate>=to_date(?, 'yyyy-MM-dd') 
    and customer0_.initDate<=to_date(?, 'yyyy-MM-dd')

**编辑[对布赖恩的回应]:**

Entity 中的类型也应该是 Calendar,对吗?我还为这两个字段添加了两个不同的 Temporal 注释。一个指向 TemporalType.TIMESTAMP,另一个指向 TemporalType.DATE。但是,将日历值作为 http 参数传递的问题。我尝试了四个版本的 URL:

1. http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10
2. http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10 13:08:24.000+0000
3. http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10T13:08:24.000+0000
4. http://localhost/webApp/customer/search/filterByDate?currentDate=1499692104

但是这些都行不通……

【问题讨论】:

  • 不确定 spring,但您可以尝试 hibernate @type 在时间戳上使用 date 类型注释
  • 它没有帮助。同样的错误。
  • 你能把整个stacktrace发帖吗?
  • 使用oracle's timestamp

标签: java rest spring-boot spring-data-jpa jpql


【解决方案1】:

没有进一步深入细节,我认为你只需要改变

@Query("SELECT c FROM Customer c where c.initDate &lt;= TO_DATE(:currentDate, 'yyyy-MM-dd') AND c.aggDate &gt;= TO_DATE(:currentDate, 'yyyy-MM-dd')")

@Query("SELECT c FROM Customer c where c.initDate &lt;= :currentDate AND c.aggDate &gt;= :currentDate")

为什么?因为字段initDateaggDate 的类型是java.util.Calendar,参数currentDate 也是。一切都匹配,Spring Data 以及任何 JPA 提供程序将 java.util.Calendar 绑定到 SQL 时间戳,如您在日志中看到的那样

...绑定参数 [8] 作为 [TIMESTAMP]...

【讨论】:

  • 我正在更新我的主帖,因为这里的空间不够。
  • @Lui,我刚刚创建了一个示例 Spring Boot 项目,它开箱即用。正如我所说:在查询中省略 TO_DATE 函数,坚持使用 URL .../customer/search/filterByDate?currentDate=2017-07-10 并使用原始问题的来源(我的意思是使用 java.util.Calendar@Temporal(TemporalType.TIMESTAMP) 作为时间戳字段)。如果您有任何问题,请告诉我。
  • 我试过这个:没有TO_DATE的查询有两种情况——第一个是@Param,没有@DateTimeFormat,第二个是没有@DateTimeFormat。在第一种情况下,我得到错误:"Failed to convert 2017-07-10 into java.util.Calendar!; ",在第二种情况下:"Parameter value [java.util.GregorianCalendar[...] did not match expected type [java.util.Date (n/a)]"。在实体中,我尝试了@Temporal 注释 - 使用TIMESTAMPDATE 以及休眠@Type。下一条评论中的其余评论...
  • 因为没有这个我得到这个:"Could not set field value [2016-02-03 11:11:27.138286] value by reflection: [class Customer.aggDate2] setter of Customer.aggDate2"。这个aggDate2 是数据库中TIMESTAMP 的类型。 initDateaggDateDATE 的类型。当我也从这个aggDate2 中删除@Type 时,我得到这个错误:"PersistentEntity must not be null"。没看懂……
  • 天哪!问题不在于日期,而在于查询和实体...在我的实际应用程序中,我有@EmbeddedId的实体我确信没有日期过滤器的查询可以正常工作...不幸的是,问题是这样@EmbeddedId...假设我有这样的实体:ENTITY。如何查看整个Entity 的数据 - 连同EmbeddedId??在日志中,提取的值是可见的,但在 REST 的响应正文中不可见。使用c.customerId, c 的查询不起作用,没有c.customerId 的查询仅返回Entity 而没有EmbeddedId
【解决方案2】:

我犯了大错...问题不在于日期,而在于查询和实体...在我的实际应用程序中,我有 @EmbeddedId 的实体,并且我确信查询没有按日期过滤工作正常......所以我没有提到它。不幸的是,问题是这个@EmbeddedId...我认为我可以使用带有c.customerId, c 的查询,它会返回所有内容,但不工作并且没有c.customerId 只返回没有EmbeddedId 的实体。

正如@Brian 在他的评论中提到的那样:

ID 应该是 JSON 响应中自身 URL 的一部分。在 JSON 对象中包含 ID 将是多余的。尽管如此,您可以通过配置公开它。详情请见this question and answer

综上所述,查询在没有TO_DATE 函数的情况下工作,日历参数使用@DateTimeFormat 注释,实体中带有日期的字段使用@Temporal 注释。

感谢您的所有帮助,并对这个错误深表歉意。

【讨论】:

  • 如果您认为您的问题已经解决,您可以接受答案并关闭问题
【解决方案3】:

尝试将模型类中的代码更改为:

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "INIT_DATE")
private java.util.Date initDate;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "AGG_DATE")
private java.util.Date aggDate;

并且在 JPQL 中没有定义 TO_DATE 函数,尝试将您的 jqpl 更改为:

@Query("SELECT c FROM Customer c where c.initDate <= :currentDate AND c.aggDate >= :currentDate"
public List<Customer> filterByDate(@Param("currentDate") Date currentDate);

【讨论】:

  • 它没有用。错误是一样的。但问题是在项目中他们希望保留日历类型。
  • 检查我更新的问题。我在那里回答,因为这里没有足够的空间。
  • @Lui 你的数据库表中INIT_DATE列和AGG_DATE列的数据类型是什么?
  • 正如我在问题中所写 - 在数据库中,一列是 DATE 类型,另一列是 TIMESTAMP 类型。但我提到了这一点,因为我有三列:2 列带有日期,1 列带有时间戳,也许我将来会需要它。目前,查询仅与 DATE 类型相关。
【解决方案4】:

JPQL 中没有TO_DATE 函数。您应该使用本机 sql 查询,或者将本机数据库函数包装到 JPQL FUNCTION 函数中:)

我认为应该是FUNCTION(TO_DATE('2000-01-01', 'YYYY-MON-DD'))

【讨论】:

  • 如果没有 TO_DATE 那么我应该能够在没有任何功能的情况下编写查询,对吧?我不需要格式化日期或其他东西。但由于某些原因,我无法强制它正常工作......
  • TO_DATE 包裹在FUNCTION() 中,如我的回答中所述。如果您不需要 - 那么您必须提供正确的@DateTimeFormat
  • 所以,事情就是这样。我有这个错误:“"Failed to convert 2017-07-10 into java.util.Calendar!",当我使用不带FUNCTION(T_DATE()) 或不带@DateTimeFormat 的查询时;同时使用两者时,错误是:"could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet; ORA-00904: "FUNCTION": invalid identifier"
  • 请在此处发布您的查询,我会在我这边尝试。
  • 看看我在回答我的问题时的解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-05
  • 2013-07-21
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-08
相关资源
最近更新 更多