如何解决使用GROUP BY的JPA Projections查询不能与空字段一起使用
我想复制一个在MySQL和H2中都能运行的查询:
SELECT
pedido_linha.produto_id AS material,COALESCE(pedido_linha.unidade_medida_id,"UN") AS uom,pedido_linha.data_pedido AS referenceDate,SUM(COALESCE(pedido_linha.quantidade,0)) AS totalQuantity,SUM(COALESCE(pedido_linha.valor_total,0)) AS totalGross,0) - COALESCE(pedido_linha.valor_descontos,0) -
COALESCE(pedido_linha.valor_impostos,0)) AS totalNet,SUM(COALESCE(pedido_linha.valor_custo,0)) AS totalCogs
FROM pedido_linha
GROUP BY pedido_linha.produto_id,pedido_linha.data_pedido,"UN");
作为示例,即使某些“分组依据”字段为空,查询也可以在MySQL中正常运行:
已进行以下尝试,以提取JPA封闭投影中的分组结果:
public interface AggregatedByMaterialUOMDate {
Produto getMaterial();
UnidadeMedida getUom();
LocalDate getReferenceDate();
Float getTotalQuantity();
Float getTotalGross();
Float getTotalNet();
Float getTotalCogs();
}
伴随存储库查询:
@Repository
public interface PedidoLinhaRepository extends JpaRepository<PedidoLinha,PedidoLinha.PedidoLinhaCompositeKey> {
@Query("SELECT pl.produto AS material,"
+ "COALESCE(pl.unidadeMedida,:unidadeMedidaPadrao) AS uom,"
+ "pl.dataPedido AS referenceDate,"
+ "SUM(COALESCE(pl.quantidade,"
+ "SUM(COALESCE(pl.valorTotal,0) - COALESCE(pl.valorDescontos,0) - COALESCE(pl.valorImpostos,"
+ "SUM(COALESCE(pl.valorCusto,0)) AS totalCogs "
+ "FROM PedidoLinha pl "
+ "GROUP BY pl.produto,pl.dataPedido,COALESCE(pl.unidadeMedida,:unidadeMedidaPadrao)")
List<AggregatedByMaterialUOMDate> consolidatedSelloutByMaterialUOMDayAtLocation(@Param("unidadeMedidaPadrao") UnidadeMedida unidadeMedidaPadrao);
}
上面的JPQL查询返回一个包含0个元素的列表,因此与先前显示的本机查询不一致。
我们试图将COALESCE(pl.unidadeMedida,:unidadeMedidaPadrao)更改为简单的pl.unidadeMedida(在SELECT和GROUP BY子句中),但没有成功。
仅当从两个子句中完全删除对pl.unidadeMedida的所有引用时,查询才成功返回所有值。
下面是PedidoLinha(在查询中由pl引用)类的示例,其中显示了对UnidadeMedida的引用:
@Getter
@Setter
@NoArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(of = "pedidoLinhaCompositeKey")
@Entity
public class PedidoLinha {
@EmbeddedId
@NonNull // torna campo obrigatório e parâmetro do construtor gerado pelo @Data (lombok)
private PedidoLinhaCompositeKey pedidoLinhaCompositeKey;
@Data // lombok: @ToString,@EqualsAndHashCode,@Getter on all fields @Setter on all non-final fields,and @RequiredArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
@Embeddable
@EqualsAndHashCode
public static class PedidoLinhaCompositeKey implements Serializable {
@NonNull // torna campo obrigatório e parâmetro do construtor gerado pelo @Data (lombok)
private String id;
@ManyToOne(optional = false,fetch = FetchType.LAZY)
@NonNull // torna campo obrigatório e parâmetro do construtor gerado pelo @Data (lombok)
private Pedido pedido;
}
@ManyToOne(optional = false)
private Produto produto;
@ManyToOne
private UnidadeMedida unidadeMedida;
public UnidadeMedida getUnidadeMedida() {
return (unidadeMedida == null) ? new UnidadeMedida("UN") : unidadeMedida;
}
// rest of the entity code
}
UnidadeMedida类
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@NoArgsConstructor
@Entity
public class UnidadeMedida {
@Id
private String id;
private String descricao;
public UnidadeMedida(String id) {
this.id = id;
}
}
可能会发生什么?我相信这可能是一个Hibernate错误,但找不到对此问题的任何引用。
解决方法
找出JPQL查询未产生任何记录的关键原因是在数据库调用中进行隐式联接的方式。
以下代码表示运行上面的JPQL查询后在数据库(How to show the last queries executed on MySQL?)中执行的实际SQL代码:
SELECT
pedidolinh0_.produto_id as col_0_0_,coalesce(pedidolinh0_.unidade_medida_id,'UN') as col_1_0_,pedidolinh0_.data_pedido as col_2_0_,sum(coalesce(pedidolinh0_.quantidade,0)) as col_3_0_,sum(coalesce(pedidolinh0_.valor_total,0)) as col_4_0_,0)-coalesce(pedidolinh0_.valor_descontos,0)-coalesce(pedidolinh0_.valor_impostos,0)) as col_5_0_,sum(coalesce(pedidolinh0_.valor_custo,0)) as col_6_0_,produto1_.id as id1_88_,produto1_.ativo as ativo2_88_,produto1_.data_descontinuacao as data_des3_88_,produto1_.data_introducao as data_int4_88_,produto1_.descricao as descrica5_88_,produto1_.ean as ean6_88_,produto1_.lote_minimo_requisicoes as lote_min7_88_,produto1_.multiplo_requisicoes as multiplo8_88_,produto1_.ncm as ncm9_88_,produto1_.unidade_medida_padrao_id as unidade10_88_
from pedido_linha pedidolinh0_
inner join produto produto1_ on pedidolinh0_.produto_id=produto1_.id
cross join unidade_medida unidademed2_
cross join pedido pedido3_
where pedidolinh0_.unidade_medida_id=unidademed2_.id
group by
pedidolinh0_.produto_id,pedidolinh0_.data_pedido,'UN')
问题在于,对SQL的转换会在查询中自动包含cross join
+ WHERE
子句pedidolinh0_.unidade_medida_id=unidademed2_.id
,实际上忽略了该字段为null的每个实例。
即使coalescing
语句中使用了select
,这些记录也已被where子句忽略,因此无效。
解决方案是在JPQL查询中强制左联接,如下所示:
SELECT
pl.produto AS material,COALESCE(um,:unidadeMedidaPadrao) AS uom,pl.dataPedido AS referenceDate,SUM(COALESCE(pl.quantidade,0)) AS totalQuantity,SUM(COALESCE(pl.valorTotal,0)) AS totalGross,0) - COALESCE(pl.valorDescontos,0) - COALESCE(pl.valorImpostos,0)) AS totalNet,SUM(COALESCE(pl.valorCusto,0)) AS totalCogs
FROM PedidoLinha pl
LEFT JOIN pl.unidadeMedida um
GROUP BY pl.produto,pl.dataPedido,pl.unidadeMedida
结果,在数据库中执行的最终SQL中,cross join
语句被left outer join
替换,并且不包含在where
子句中。
对于为什么在Hibernate中做出此设计决策(使用交叉联接+ where语句代替左联接)的任何见解,将不胜感激。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。