【问题标题】:How to convert joda DateTime in jpa native query如何在 jpa 本机查询中转换 joda DateTime
【发布时间】:2013-10-08 07:40:39
【问题描述】:

我有一个具有 DateTime 属性的实体在休眠状态下持久化

@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
@Column(name = "EFF_DT")
protected DateTime effDt;

这一切都很好,适用于常规 spring-data-jpa 生成的查询。

我正在尝试添加自定义本机查询

 @Query(value = "SELECT COUNT(*) FROM wsa_circuit_state_history ch WHERE ch.eff_dt between ?1 and ?2", nativeQuery = true)
    Integer countEffDateBetween(DateTime start, DateTime end);

我得到的错误是尝试调用它时

 java.sql.SQLException: ORA-00932: inconsistent datatypes: expected DATE got BINARY

这是我在将自定义类型映射添加到我的实体之前使用常规 spring-data 查找器时遇到的相同错误

 @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")

如何让 spring-data-jpa/hibernate 使用自定义类型映射来将参数映射到本机查询?

【问题讨论】:

  • 你找到答案了吗?
  • @WojciechGórski 不,我没有找到解决办法。最后我设法在 JPA 中重写了查询,然后休眠 @Type 注释开始处理类型转换 @Query("select count(ch.circuitId) from nz.co.vodafone.wcim.model.CircuitStateLog ch where ch.effDt between ?1 and ?2 and ch.state=?3")

标签: hibernate jpa jodatime spring-data-jpa


【解决方案1】:

我也有同样的要求。我通过将自定义类型注册到休眠配置来解决它。

对于原生查询,TypeDefs 不足以让 hibernate 找到用户类型。它需要类型解析器来找到它的类型。

@Query(value = "SELECT COUNT(*) FROM wsa_circuit_state_history ch WHERE ch.eff_dt between ?1 and ?2", nativeQuery = true)
    Integer countEffDateBetween(DateTime start, DateTime end);

在这种情况下,hibernate 会尝试从类型解析器中猜测类型(org.joda.time.DateTime)。

Type type = session.getFactory().getTypeResolver().heuristicType(typename);

由于 DateTime 没有解析器。因此它正在获取默认类型(可序列化类型)。并且 DateTime 的值被强制转换为它的类型,即序列化并保留在 DB 中,由于大二进制值而失败.

要解决这个问题,您需要将 DateTime 类型注册到 Hibernate Configuration 为

configuration.registerTypeOverride(new org.jadira.usertype.dateandtime.joda.PersistentDateTime(), new String[]{"org.joda.time.DateTime"});

【讨论】:

    【解决方案2】:

    在类似的情况下,我编写了一个自定义 CompositeUserType,它将 Joda DateTime 存储在两个字段中,一个日期时间字段和一个时区字段。

    当实体被保存时,它以 UTC 格式存储时间值,并将时区作为原始值存储,在检索时它执行相反的操作(即,它将存储在数据库中的时间转换回原始时区)。

    正如您在上面所指出的,尽管像“dateTime BETWEEN ?1 AND ?2”这样的查询具有直观意义,但就复合类型而言当然没有意义,因为您需要牢记时区。这就是我以 UTC 格式存储所有值的原因,因此我可以随时比较数据库中的任意两个日期/时间值。

    不幸的是,这意味着我传递的所有日期/时间查询参数都必须转换为 UTC。

    【讨论】:

      【解决方案3】:

      只需像这样将您的属性设置为 java.util.Date

      @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
      @Column(name = "EFF_DT")
      @Temporal( TemporalType.TIMESTAMP )
      protected Date effDt;
      

      然后在你的 getter/setter 中转换

      public DateTime getEffDt() {
          return null == effDt ? null : new DateTime( effDt );
      }
      
      public void setEffDt( final DateTime effDt ) {
          this.effDt = null == effDt ? null : effDt.toDate();
      }
      

      本地人:

      @Query(value = "SELECT COUNT(*) FROM wsa_circuit_state_history ch WHERE ch.eff_dt between ?1 and ?2", nativeQuery = true)
      Integer countEffDateBetween(Date start, Date end);
      

      【讨论】:

      • 这是一个避免使用 DateTime 的有趣解决方法,但在我们的例子中,我们希望将该字段保留为 DateTime。
      • 为什么不能把countEffDateBetween(DateTime start, DateTime end);改成countEffDateBetween(Date start, Date end);?这将使您能够将属性保留为 DateTime。
      • 我们使用的是 DateTime,而不是 java.util.Date。当然,如果我们停止使用 DateTime 并使用 Date,我们就不会遇到这个问题。但是,我们希望使用更出色的 joda DateTime API。我的问题是“我如何使本机查询与 DateTime 一起工作”,不使用 DateTime 不是这个问题的解决方案。
      • 另外,我不确定使用“日期开始”是否有效,因为 EffDate 字段的类型为 DateTime,那么查询的参数必须是相同的类型,否则 spring-data 不会了解如何从接口声明编写查询。可能是 spring-data 足够聪明,可以在 Date 和 DateTime 之间进行转换,但无论如何,当 API 的其余部分使用 DateTime 时,我不想强​​制接口用户传递 Date 对象。
      猜你喜欢
      • 1970-01-01
      • 2011-06-07
      • 2019-01-11
      • 1970-01-01
      • 2021-03-13
      • 1970-01-01
      • 1970-01-01
      • 2015-04-02
      • 1970-01-01
      相关资源
      最近更新 更多