Spring JPA,具有将错误列映射到投影接口的本机查询和数据投影

时间:2018-02-10 23:05:29

标签: spring hibernate spring-data-jpa

我有一个奇怪的问题,我无法弄清楚它为什么会发生。我确定我做错了,因为这是我第一次使用数据投影而且我从来没有使用过DTO这样的问题。

我有一个SELECT状态员返回各种数据类型的某些列。我有一个接口,我将其传递给JPA存储库,以便它可以进行接口映射。但是,不是根据列名映射结果(例如,' accountnum' - > getAccountnumber()),而是按字母顺序映射列。所以,如果' date_of_order'是SELECT语句中的第一个,它的值将由getAccountnumber()返回。

我有一个看起来像这样的投影界面:

public interface FlatSearchResult {
    String getAccountnumber();
    UUID getTrackingId;
    Date getDateOfOrder;
}

我的模型有三个表格如下:

ACCOUNT
  - account_id : uuid (pkey)
  - accountnumber : string
ORDERS
  - order_id : uuid (pkey)
  - date_of_order : timestamp
  - account_id : uuid (fkey)
TRACKING
  - tracking_id : uuid (pkey)
  - order_id : uuid (fkey)

每个表中都有其他列,但它们并不相关。

我有一个使用简单查询定义的存储库:

public interface OrderTrackingRepository extends JpaRepository<Account, UUID> {
  @Query( nativeQuery = true,
          value = "SELECT o.date_of_order, a.accountnumber, t.tracking_id " +
                  "FROM account as a " +
                  "INNER JOIN orders as o USING (account_id) " +
                  "INNER JOIN tracking as t USING (tracking_id) " +
                  "WHERE a.accountnumber = :acctnum")
  <T> Collection<T> findOrderInfoForAccount(@Param("acctnum") acctNumber, Class<T> type);
}

当我调用此方法时,查询返回正确的行。但是,不是使用列名称进行映射(例如,date_of_order到getDateOfOrder()),而是基于SELECT语句中列的顺序映射到接口中按字母顺序排序的方法。

所以:

SELECT date_of_order, accountnumber, tracking_id

结果:

getAccountNumber() -> date_of_order
getDateOfOrder() -> accountnumber
getTrackingId() -> tracking_id

它会以这种方式不断回归,所以它不是一个短暂的问题。

作为临时解决方法,我在SELECT语句中对列进行了重新排序。但我宁愿不必这样做,因为它就像迭代结果集并依赖列位置一样,这让我感到抽搐......

如何让Spring JPA从结果集映射到我的界面?我是否需要使用某些东西来注释我的投影界面方法,以告诉Spring它所指的列名称是什么?

我的数据库是Postgres。我使用的是Spring 5.0.2.RELEASE和Spring-Boot 2.0.0.M7。如果需要,我可以将其中任何一个调整为较新的版本,但不能更新。我使用C3P0 0.9.5.2进行连接池,并使用postgres-9.2-1002.jdbc4。所有其他依赖项(休眠等)都是这个版本的Spring-Boot所引入的。

3 个答案:

答案 0 :(得分:4)

不确定这是否是正确的解决方案,因为它只适合80%的描述。但评论太长了。所以我们走了。

我认为你误解了@ osamayaccoub或文档。你的房产名称没问题。但是select中的列应该与java约定匹配。

所以第一次尝试解决这个问题就是

value = "SELECT o.date_of_order as dateOfOrder, a.accountnumber as accountNumber, t.tracking_id as trackingId "

注意:这可能确实有效,但可能会稍后破坏,所以请继续阅读,即使它确实有效

但Postgres会将所有不加引号的内容转换为小写字母(Oracle和MySql会做类似的事情虽然细节有所不同,但还不了解其他数据库)。所以你真的应该使用:

value = "SELECT o.date_of_order as \"dateOfOrder\", a.accountnumber as \"accountNumber\", t.tracking_id as \"trackingId\" "

这可能不起作用,因为您正在使用has a bug的Hibernate版本,因为它将所有内容转换为小写。

所以你应该升级到最新的Hibernate版本5.3.13,它已经解决了这个问题。

这个错误修复有趣的可能会打破没有双引号的版本。 但是对this PR Spring Data JPA issue,它应该再次使用this

我不理解的部分是,为什么使用列顺序分配内容。

答案 1 :(得分:2)

我有同样的问题,我通过按字母顺序查询查询列来解决。

在你的情况下:

public interface OrderTrackingRepository extends JpaRepository<Account, UUID> {
  @Query( nativeQuery = true,
          value = "SELECT a.accountnumber, o.date_of_order, t.tracking_id " +
                  "FROM account as a " +
                  "INNER JOIN orders as o USING (account_id) " +
                  "INNER JOIN tracking as t USING (tracking_id) " +
                  "WHERE a.accountnumber = :acctnum")
  <T> Collection<T> findOrderInfoForAccount(@Param("acctnum") acctNumber, Class<T> type);
}

所以你会得到:

getAccountNumber() - &gt;帐号 getDateOfOrder() - &gt; date_of_order getTrackingId() - &gt; TRACKING_ID

答案 2 :(得分:-2)

Hibernate 按字母顺序对查询进行排序,因此您必须将选项更改为:“SELECT a.accountnumber, o.date_of_order, t.tracking_id ...”并且界面的getter应遵循相同的字母顺序< /强>