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

标签 jpa spring-data-jpa spring-data jpql spring-repositories

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

enter image description here

    @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)"
        )

enter image description here

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

最佳答案

我认为在关系代数方面,您要求的是ActorContent的集合减去由actor = actor和meanOfPayment = meanOfPayment和createDate ActorContent的集合。因此,考虑的方法是从ActorContentac1.meanOfPayment = ac2.meanOfPayment and ac1.actor = ac2.actor and ac1.createDate < ac2.createDate的叉积获得第二组。然后从ActorContent的集合中减去该集合。我没有看过它是否比使用MAXGroup 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)")

这使我在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]

之后,您可能需要通过联合获取ActorMeanOfPayment实例来优化查询。举例来说:
@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)")

这将导致以下休眠生成的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)

当然,如果您需要特定的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);

这给了我“前三行”
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]

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

这些是我使用过的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;

关于jpa - 如何在Spring JpaRepository中使用JPQL选择组中的最新记录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52744463/

相关文章:

java - Spring @Repository 应该遵循 JpaRepository 吗?

Spring 数据: relationships between 2 different data sources

java - Spring Data Jpa findBy... 和 findBy...Equals 之间的区别

java - 删除一对一关系上的 jpa 实体

java - @Autowire如何在不使用@Bean注解的情况下获取spring bean

java - Spring Data PageImpl 没有返回正确大小的页面?

spring-boot - 使用 JPA native 查询在 jsonb(JSON 数组)列中进行查询

java - 在保存 Spring Data MongoDB 实体之前对 MongoDB 文档进行后处理

java - Spring Boot JPA如何处理子表更新

java - 在使用 AbstractRoutingDataSource 切换数据源时共享事务