简体   繁体   English

JPA标准-依靠WHERE子句

[英]JPA Criteria - Count on WHERE Clause

I'm trying to create the following query in a JPA Criteria Query: 我正在尝试在JPA标准查询中创建以下查询:

SELECT V.* FROM VENDA V
WHERE 
( SELECT COUNT(D.ID) FROM VENDADETALHE D WHERE D.ID IN
( SELECT CVD.DETALHES_ID FROM VENDA_VENDADETALHE CVD WHERE CVD.VENDA_ID = V.ID ) 
AND ( SELECT I.TIPO FROM ITEM I WHERE I.ID=D.IDITEM ) <> 'SERVICO' ) = 0
AND
( SELECT COUNT(D.ID) FROM VENDADETALHE D WHERE D.ID IN
( SELECT CVD.DETALHES_ID FROM VENDA_VENDADETALHE CVD WHERE CVD.VENDA_ID = V.ID ) 
AND ( SELECT I.TIPO FROM ITEM I WHERE I.ID=D.IDITEM ) = 'SERVICO' ) > 0

I have the following class structure 我有以下课程结构

public class Venda {
  /* other class attributes ( id & etc... ) */
  @OneToMany( orphanRemoval=true, cascade = CascadeType.ALL, fetch =FetchType.LAZY )
  @JoinTable
  private List<VendaDetalhe> detalhes;
}

public class VendaDetalhe{
  /* other class attributes */
  @ManyToOne
  @JoinColumn( name = "iditem", referencedColumnName = "id")
  private Item item;
}

public class Item{
  /* other class attributes */
  @Enumerated( EnumType.STRING )
  private ETipo tipo;
}
public enum ETipo{
  PRODUTO,
  SERVICO;
}

And the following code for the query: 和以下代码进行查询:

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Venda> qry = builder.createQuery( Venda.class );
Root<Venda> root = qry.from( Venda.class );
Join<Venda,VendaDetalhe> join = root.join( "detalhes", JoinType.INNER );
List<Predicate> p = new ArrayList<>();
p.add( builder.greatherThan( builder.count( builder.equal( join.get( "item" ).get("tipo"), ETipo.SERVICO ) ), 0L ) );
p.add( builder.equal( builder.count( builder.equal( join.get("item" ).get("tipo"),ETipo.PRODUTO) ), 0L) );
qry.where( p.toArray( new Predicate[ p.size() ] );
em.createQuery( qry ).getResultList();

But this generates an QuerySyntaxException , saying that a CLOSE is expected but = found. 但这会生成一个QuerySyntaxException ,表示需要CLOSE=找到。 Is my CriteriaQuery syntax correct? 我的CriteriaQuery语法正确吗? I've searched the internet about using count on the WHERE clause, but couldn't find anything. 我已经在网上搜索了有关在WHERE子句上使用count信息,但找不到任何东西。

The exception message is the following: 异常消息如下:

org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found '=' near line 1, 
column 249 
[select generatedAlias0 from
 br.com.koinonia.habil.model.user.movimentacoes.compraevenda.Venda 
as generatedAlias0 inner join generatedAlias0.detalhes as generatedAlias1 where 
( generatedAlias0.empresa=:param0 ) and ( count(generatedAlias1.item.tipo=:param1)>0L ) 
and ( count(generatedAlias1.item.tipo=:param2)=0L )]

Don't run a count query when you could run an exists query . 当您可以运行存在查询时,请勿运行计数查询 Even when you stay within the SQL language (or any language that translates to SQL, such as JPQL), chances are that your optimiser might not be able to pick up the possibility of transforming COUNT(*) == 0 to NOT EXISTS() and COUNT(*) > 0 to EXISTS() . 即使您停留在SQL语言(或任何翻译成SQL的语言,例如JPQL)中,您的优化程序也可能无法接受将COUNT(*) == 0NOT EXISTS()的可能性。并且COUNT(*) > 0EXISTS() Imagine one of the counts results in 1 million. 想象其中一个计数结果为100万。 Do you really need for the database to figure out the exact count value? 您是否真的需要数据库找出确切的计数值? Or can the database stop as soon as it knows whether there exists (or not) a given row? 还是数据库一旦知道给定行是否存在就可以停止?

Your original query could be re-written to this: 您的原始查询可以重写为:

SELECT V.* 
FROM VENDA V
WHERE NOT EXISTS ( 
  SELECT 1 
  FROM VENDADETALHE D 
  WHERE D.ID IN ( 
    SELECT CVD.DETALHES_ID 
    FROM VENDA_VENDADETALHE CVD 
    WHERE CVD.VENDA_ID = V.ID 
  ) 
  AND ( 
    SELECT I.TIPO FROM ITEM I WHERE I.ID=D.IDITEM 
  ) <> 'SERVICO' 
)
AND EXISTS ( 
  SELECT 1 
  FROM VENDADETALHE D 
  WHERE D.ID IN ( 
    SELECT CVD.DETALHES_ID 
    FROM VENDA_VENDADETALHE CVD 
    WHERE CVD.VENDA_ID = V.ID 
  ) 
  AND ( 
    SELECT I.TIPO FROM ITEM I WHERE I.ID=D.IDITEM 
  ) = 'SERVICO' 
)

Of course, your correlated subqueries that are compared with 'SERVICO' could be further transformed to inner joins, but I'm not sure if that's causing trouble here. 当然,与'SERVICO'进行比较的相关子查询可以进一步转换为内部联接,但是我不确定这是否在这里引起麻烦。

Perhaps, this is now easier for you to write in JPQL, but why not just run a SQL query through EntityManager.createNativeQuery(String, Class) . 也许,这现在使您更容易用JPQL编写代码,但是为什么不通过EntityManager.createNativeQuery(String, Class)运行SQL查询。 Since you're projecting an entity ( V.* ), this would work just fine. 由于您要投影实体( V.* ),因此可以正常工作。

count in Hibernate QL is apparently very finicky about what goes inside the function and cannot accept nested queries. Hibernate QL中的count显然对函数内部的内容非常挑剔,并且不能接受嵌套查询。 So this line: 所以这行:

builder.count( builder.equal( join.get( "item" ).get("tipo"), ETipo.SERVICO ) )

Needs to be peeled apart a bit so that the equal comparison is somewhere else and count is just called on a single query. 需要剥离一下,以便equal比较位于其他位置,并且仅在单个查询上调用count

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM