Spring Data JPA查询表现异常

3

我有以下实体:

public class Order_status_sas {
   private Order_sas order;
   private Date lastModified;
   ...
}

public class Order_sas {
   private long id;
   ...
}

我的CrudRepository:

public interface StatusesWareHouseRepository extends CrudRepository<Order_status_sas, Long> {
   Order_status_sas findFirstByOrderIdOrderByLastModifiedDesc(long id);
}

我期望这个方法findFirstByOrderIdOrderByLastModifiedDesc会从表格Order_status_sas中返回第一行数据,其中order.id = <some_id>,并按照lastModified字段排序,但是在日志中我看到了这个查询:

Hibernate: select ... 
       from order_status_sas a 
          left outer join orders_sas b 
            on a.order_id=b.id 
       where b.id=? 
       order by a.last_modified desc

这个查询不会返回一行,而是返回一组行。似乎Spring Data没有在我的方法名中查看单词First。同时,我得到了一个异常:

org.springframework.dao.IncorrectResultSizeDataAccessException: 
result returns more than one elements; nested exception is javax.persistence.NonUniqueResultException: result returns more than one elements

请告诉我我做错了什么,以及如何实现我的目标?
编辑过的内容: 我用自定义查询编辑了我的StatusesWareHouseRepository:
@Query("select s from Order_status_sas s where s.order.id = ?1 order by s.lastModified desc limit 1")
Order_status_sas findFirstByOrderIdOrderByLastModifiedDesc(long id);

但是由Hibernate执行的查询语句并没有改变。它看起来像这样:

select ... 
from order_status_sas s 
where s.order_id=? 
order by s.last_modified desc

实际上,它看起来非常像Spring Data正在查看您方法名称中的单词“first”,否则您将不会获得“IncorrectResultSizeDataAccessException”。如果您没有修改域模型的能力,您可能需要使用“Criteria”查询或手动查询。 - avgvstvs
2个回答

2

好的,我理解了@PriduNeemre的观点。让我们离开数据库模型,回到JPA的问题。这里有另一个例子:

@Entity
public class Client {
   ....
}

public interface ClientRepository extends CrudRepository<Client, Integer> {

Client findFirstByOrderByNameDesc();
}

Hibernate查询仍然像这样:

select ... 
from clients c 
order by c.name desc

正如我之前所说,这个(以及之前的)查询唯一缺少的是SQL的LIMIT 1子句。至于如何实现或是否可以实现 - 希望有更深入了解Spring新功能的人会来帮忙。但在此期间,您可以使用之前提到的一些解决方案和变通方法。 - Priidu Neemre

0
你尝试过在 findFirstByOrderIdOrderByLastModifiedDesc(..) 方法上方添加一个 @Query 注解(参见 这里)手动指定期望的行为吗?以下是一个(无关的)示例,说明如何使用它:
public interface InvoiceRepository extends JpaRepository<Invoice, Long> {

    @Query("SELECT I FROM Invoice I JOIN I.customer C JOIN C.user U WHERE 
        U.username = :username")
    public List<Invoice> findInvoicesByUsername(@Param("username")
        String username);
}

请注意,注释正文中使用的查询语言实际上是JPQL,而不是SQL。有关@Query注释的更多示例,请参阅Spring Data文档这里

附注:对于您的领域对象结构,我也有矛盾的感觉,即是否应该将Order_sas的实例真正存储在Order_status_sas的实例中 - 是否应该反过来呢?通常,您会希望将引用对象存储在主要领域对象中,而不是反过来。(不过,我可能没有理解正确,还有一丝可能性。

编辑:基于您当前的领域模型,我甚至可以说,Hibernate做的一切都是正确的,只是缺少一个LIMIT 1子句,将预期结果集限制为一行。然而,查询本身非常低效,并且可以通过修复倾斜的领域模型来改进。


如果我使用@Query,当然可以实现预期的行为,但是我想使用这种方法,因为根据文档,我可以实现这个目的。关于我的对象结构,我不同意你的看法。这是一对多的关系,其中一个订单有多个订单状态。我想知道我做错了什么,导致我的方法没有按照文档中所述的那样运行。 - polis
我认为它的行为不正确,是因为您提供了一个非标准的领域模型。当然,“订单状态”也可以连接到多个“订单”,因此您不再处理一对多关系,而是处理多对多关系。处理多对多关系的标准程序是通过创建联接表来完成的,我相信您已经知道了这一点。因此,“标准”的领域模型将包括以下内容:Order_sasOrder_status_link_sas(联接表),Order_status_sas - Priidu Neemre
请注意,Order_status_link_sas 不是联接表的完美名称,而只是一个快速示例。通过一些猜测,您可能会想出更好、更有意义的名称。 - Priidu Neemre
我真的不明白为什么我的模型是非标准的。请看,它完全就像这里显示的一样:OneToMany。请解释一下,为什么你认为我在处理多对多关系? - polis
@polis:我认为我已经对你的数据模型问题给出了相当全面的解释。然而,让我再试一次:你提供的示例确实描述了一对多关系。涉及到的关系的口头描述是:“一个员工可以拥有多个电话;一个电话可以属于一个员工”。然而,在你的情况下,这种关系可以表述为:“一个订单可以由多个订单状态描述;一个订单状态可以归属于多个订单”。因此,你有效地创建了一个多对多关系。 - Priidu Neemre
@polis:不过,有点不太清楚的是,为什么您希望一个“订单”同时连接到多个“订单状态”。在大多数情况下,您会有以下描述:“一个订单可以由一个订单状态(每次)描述;一个订单状态可以归属于多个订单”,这又是一种一对多的关系。但有时候,您可能希望存储历史数据,在这种情况下,前面提到的多对多关系就会发挥作用。 - Priidu Neemre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接