如何在 Spring JpaRepository 中使用 JPQL select 组中的最新记录?
How to select latest record in group using JPQL in Spring JpaRepository?
在 SpringBoot 微服务中,我正在尝试 select 每个 mean_of_payment_id 的演员的最新记录。为实现这一点,selecting actor content for actor_id 其中 created_date 等于 max(created_date) 的嵌套查询的子集,使用 [=23 上的 group by 子句=].我正在使用 JPQL。下面是 table 结构和查询。
@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)"
)
不幸的是,执行查询后我得到了所有记录,但我期望的是前三行。 MeanOfPayment 和 Actor 是 ActorContent 的参考 table。
我认为就关系代数而言,您要求 ActorContent
减去 ActorContent
的集合,该集合受 actor = actor 和 meanOfPayment 约束= meanOfPayment 和 createDate < createDate。所以,想起来的方法是从 ActorContent
与 ac1.meanOfPayment = ac2.meanOfPayment and ac1.actor = ac2.actor and ac1.createDate < ac2.createDate
的叉积中得到第二组。然后从ActorContent
的集合中减去这个集合。我没有看它是否比使用 MAX
和 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)")
这给了我 UPPER table 中的前四行,代表第一个演员和他唯一的 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]
之后,您可能希望通过联合获取 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)")
这导致以下休眠生成 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);
这给了我 "top three rows"
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 组合具有相同的创建日期有疑问,那么您可以通过几种不同的方式进行处理。首先,如果您有一个不想处理这些重复项的逻辑约束,那么您可能也应该有一个数据库约束,这样您就不会得到它们并确保您不会首先创建它们。另一件事是您可以手动检查结果列表并将其删除。最后,您可以在查询中使用 distinct,但必须省略 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;
在 SpringBoot 微服务中,我正在尝试 select 每个 mean_of_payment_id 的演员的最新记录。为实现这一点,selecting actor content for actor_id 其中 created_date 等于 max(created_date) 的嵌套查询的子集,使用 [=23 上的 group by 子句=].我正在使用 JPQL。下面是 table 结构和查询。
@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)"
)
不幸的是,执行查询后我得到了所有记录,但我期望的是前三行。 MeanOfPayment 和 Actor 是 ActorContent 的参考 table。
我认为就关系代数而言,您要求 ActorContent
减去 ActorContent
的集合,该集合受 actor = actor 和 meanOfPayment 约束= meanOfPayment 和 createDate < createDate。所以,想起来的方法是从 ActorContent
与 ac1.meanOfPayment = ac2.meanOfPayment and ac1.actor = ac2.actor and ac1.createDate < ac2.createDate
的叉积中得到第二组。然后从ActorContent
的集合中减去这个集合。我没有看它是否比使用 MAX
和 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)")
这给了我 UPPER table 中的前四行,代表第一个演员和他唯一的 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]
之后,您可能希望通过联合获取 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)")
这导致以下休眠生成 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);
这给了我 "top three rows"
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 组合具有相同的创建日期有疑问,那么您可以通过几种不同的方式进行处理。首先,如果您有一个不想处理这些重复项的逻辑约束,那么您可能也应该有一个数据库约束,这样您就不会得到它们并确保您不会首先创建它们。另一件事是您可以手动检查结果列表并将其删除。最后,您可以在查询中使用 distinct,但必须省略 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;