【问题标题】:Pass date parameter to native query将日期参数传递给本机查询
【发布时间】:2017-12-06 13:12:44
【问题描述】:

用户可以根据出现值执行操作。当此值等于“DAILY”时,我想检索过去 24 小时内未完成的所有日常操作。

有效的 SQL 查询:

SELECT distinct a.* FROM action as a LEFT OUTER JOIN history as h
ON a.id = h.action_id
AND h.user_id= <user> WHERE a.occurrence = 'DAILY' AND (h.id is NULL OR h.entry_date < TIMESTAMP 'yesterday')

等效的原生查询:

@Query(value = 
        "SELECT distinct a.* FROM action a "
        + "LEFT OUTER JOIN history h "
        + "ON a.id = h.action_id "
        + "AND h.user_id = :userId "
        + "WHERE a.occurrence='DAILY' AND (h.id IS NULL OR h.entry_date < :yesterday) ", nativeQuery = true)
public List<Action> findAllAvailableActions(@Param("userId") Long userId, @Param("yesterday") ZonedDateTime yesterday);

如何在我的服务中调用它:

ZonedDateTime today = ZonedDateTime.now(ZoneOffset.UTC);
ZonedDateTime yesterday = today.minus(1,ChronoUnit.DAYS);
Long userId = userDTO.getId();
List<Action> result = actionRepositoryCustom.findAllAvailableActions(userId, yesterday);

但是,我在测试中确实得到了错误的结果(已完成的操作被返回)。恐怕这与日期参数有关。属性 entry_date 在我的实体中被声明为 ZoneDateTime。我做错了什么?

休眠:5.2.4

【问题讨论】:

  • entry_date在数据库中是如何声明的?数据库中的entry_date是什么类型?还有数据库中的entry_date是保存在UTC时区的吗?
  • entry_date 在我的数据库中被描述为 timestamp without time zone。我确实使用 Java 中的以下语法保存它:historyDTO.setEntryDate(ZonedDateTime.now(ZoneOffset.UTC));

标签: spring postgresql hibernate jpa


【解决方案1】:

您不能将 ZonedDateTime 传递到本机 SQL 查询中。您需要将其转换为日历:

@Query(value = 
    "SELECT distinct a.* FROM action a "
    + "LEFT OUTER JOIN history h "
    + "ON a.id = h.action_id "
    + "AND h.user_id = :userId "
    + "WHERE a.occurrence='DAILY' AND (h.id IS NULL OR h.entry_date < :yesterday)", nativeQuery = true)
public List<Action> findAllAvailableActions(@Param("userId") Long userId, @Param("yesterday") Calendar yesterday);

您可以通过这种方式转换您的 ZonedDateTime:

public Calendar convertToDatabaseColumn(ZonedDateTime entityAttribute) {
    if (entityAttribute == null) {
        return null;
    }

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(entityAttribute.toInstant().toEpochMilli());
    calendar.setTimeZone(TimeZone.getTimeZone(entityAttribute.getZone()));
    return calendar;
}

这里描述了这种方法:link

【讨论】:

  • 你写的帖子给我留下了深刻的印象(我想因为我不懂俄语:p)当时我遇到了这个问题,因为我无法解决它,我使用了而是存储过程,但确实您的解决方案看起来更干净。我同意这个逻辑,我已经在另一个函数上尝试过,结果看起来是正确的。谢谢@dimirsen
  • 不客气!我的英语足以回答相当复杂的问题:) 我最近碰巧遇到了同样的问题,并决定分享我的解决方案。
  • 如果时间部分不重要,您也可以使用 ZonedDateTime (toLocalDate()) 中的本地日期部分来代替日历。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-19
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多