如何在Spring JpaRepository中使用JPQL选择组中的最新记录?

ajk*_*ush 8 jpa jpql spring-data spring-data-jpa spring-repositories

在SpringBoot微服务中,我试图为每个mean_of_payment_id选择一个actor的最新记录.为此,请使用mean_of_payment_id上的group by子句为actor_id选择actor内容,其中created_date等于max(created_date)嵌套查询的子集.我正在使用JPQL.下面是表结构和查询.

在此输入图像描述

    @Query("select ac from ActorContent ac "
        + "where (ac.actor.uuid=:actorUuid ) and "
        + "ac.createdDate IN ( SELECT MAX(aci.createdDate) "
            + "FROM ActorContent aci WHERE ac.actor.uuid=aci.actor.uuid "
            + "and aci.uuid = ac.uuid group by ac.meanOfPayment.id)"
        )
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

不幸的是,在执行查询后,我得到了所有记录,但我期待的是前三行.MeanOfPayment和Actor是ActorContent的引用表.

K.N*_*las 4

我认为就关系代数而言,您要求的集合ActorContent 减去ActorContentactor = actor 和meanOfPayment = MeanOfPayment 和 createDate < createDate 约束的集合。因此,思考它的方法是从 的叉积中获得第二ActorContentac1.meanOfPayment = ac2.meanOfPayment and ac1.actor = ac2.actor and ac1.createDate < ac2.createDate。然后从 的集合中减去这个集合ActorContent。我还没有考虑它是否比使用MAXand更有效Group By,例如:

@Query("select ac from ActorContent ac where ac.id not in (select ac1.id from ActorContent ac1, ActorContent ac2 where ac1.meanOfPayment = ac2.meanOfPayment and ac1.actor = ac2.actor and ac1.createDate < ac2.createDate)")
Run Code Online (Sandbox Code Playgroud)

这为我提供了 UPPER 表中的前四行,代表第一个演员和他唯一的meanOfPayment,以及第二个演员和他对所有三个meanOfPayments 的最近付款。

ActorContent [id=1, actor=Actor [id=1], meanOfPayment=MeanOfPayment [id=1], amount=10500.00, createDate=2018-10-09 00:00:00.887]
ActorContent [id=2, actor=Actor [id=2], meanOfPayment=MeanOfPayment [id=1], amount=-10400.00, createDate=2018-10-02 00:00:00.887]
ActorContent [id=3, actor=Actor [id=2], meanOfPayment=MeanOfPayment [id=3], amount=6000.00, createDate=2018-10-02 00:00:00.887]
ActorContent [id=4, actor=Actor [id=2], meanOfPayment=MeanOfPayment [id=2], amount=200.00, createDate=2018-09-30 00:00:00.887]
Run Code Online (Sandbox Code Playgroud)

Actor之后,您可能希望通过连接获取和实例来优化查询MeanOfPayment。举例来说:

@Query("select ac from ActorContent ac left outer join fetch ac.actor left outer join fetch ac.meanOfPayment where ac.id not in (select ac1.id from ActorContent ac1, ActorContent ac2 where ac1.meanOfPayment = ac2.meanOfPayment and ac1.actor = ac2.actor and ac1.createDate < ac2.createDate)")
Run Code Online (Sandbox Code Playgroud)

这会产生以下 hibernate 生成的 SQL 查询:

select actorconte0_.id as id1_1_0_, actor1_.id as id1_0_1_, meanofpaym2_.id as id1_2_2_, actorconte0_.actor_id as actor_id4_1_0_, actorconte0_.amount as amount2_1_0_, actorconte0_.create_date as create_d3_1_0_, actorconte0_.mean_of_payment_id as mean_of_5_1_0_ from actor_content actorconte0_ left outer join actor actor1_ on actorconte0_.actor_id=actor1_.id left outer join mean_of_payment meanofpaym2_ on actorconte0_.mean_of_payment_id=meanofpaym2_.id where actorconte0_.id not in  (select actorconte3_.id from actor_content actorconte3_ cross join actor_content actorconte4_ where actorconte3_.mean_of_payment_id=actorconte4_.mean_of_payment_id and actorconte3_.actor_id=actorconte4_.actor_id and actorconte3_.create_date<actorconte4_.create_date)
Run Code Online (Sandbox Code Playgroud)

当然,如果您想要特定的,Actor只需将其添加到 where 子句中即可。

@Query("select ac from ActorContent ac left outer join fetch ac.actor left outer join fetch ac.meanOfPayment where ac.actor.id = :actorId and ac.id not in (select ac1.id from ActorContent ac1, ActorContent ac2 where ac1.meanOfPayment = ac2.meanOfPayment and ac1.actor = ac2.actor and ac1.createDate < ac2.createDate)")
public List<ActorContent> findLatestForActor(@Param("actorId") Integer actorId);
Run Code Online (Sandbox Code Playgroud)

这给了我“前三行”

ActorContent [id=2, actor=Actor [id=2], meanOfPayment=MeanOfPayment [id=1], amount=-10400.00, createDate=2018-10-02 00:00:00.066]
ActorContent [id=3, actor=Actor [id=2], meanOfPayment=MeanOfPayment [id=3], amount=6000.00, createDate=2018-10-02 00:00:00.066]
ActorContent [id=4, actor=Actor [id=2], meanOfPayment=MeanOfPayment [id=2], amount=200.00, createDate=2018-09-30 00:00:00.066]
Run Code Online (Sandbox Code Playgroud)

如果您对 Actor 和 MeanOfPayment 组合具有相同的 createDate 存在疑问,那么您可以通过几种不同的方式进行处理。首先,如果您有一个逻辑约束,因此您不想处理这些重复项,那么您可能也应该有一个数据库约束,这样您就不会获取它们并确保您不会首先创建它们。另一件事是您可以手动检查结果列表并将其删除。最后,您可以在查询中使用不同的值,但必须省略ActorContentid 字段,因为它不是唯一的。您可以使用 DTO 来完成此操作,但 JPA 无法处理投影,join fetch因此您只能获得 actor.id 和meanOfPayment.id,否则您将进行多个选择。在这个用例中,多重选择可能不是交易杀手,但你必须自己决定这一切。当然,您也可以将 actor.id、meanOfPayment.id 和 createDate 的组合作为主键ActorContent,这将具有成为上述约束的额外好处。

这些是Entities我一起工作过的。

@Entity
public class Actor {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;

@Entity
public class MeanOfPayment {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;

@Entity
public class ActorContent {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;

    @ManyToOne
    private Actor actor;
    @ManyToOne
    private MeanOfPayment meanOfPayment;

    private BigDecimal amount;
    @Temporal(TemporalType.TIMESTAMP)
    private Date createDate;
Run Code Online (Sandbox Code Playgroud)