JPA - 带有Left Join和Count的TypedQuery

时间:2017-03-01 21:52:52

标签: java database hibernate jpa hql

我一直在尝试使用带有Criteria Builder的TypedQuery构建一个SQL:

select
        a.id,
        a.numeroAvisoPagamento,
        a.industria_id,
        a.varejo_id,
        a.dataAvisoPagamento,
        a.statusAvisoPagamento,
        a.dataUploadArquivo,
        a.dataImportacaoArquivo,
        a.dataConciliacaoAviso,
        count(c.avisoPagamento_id) as qtdeNotas, 
    from
        AvisoPagamento a  
    left join
        LoteAvisoPagamento l 
            ON l.codigoAviso = a.numeroAvisoPagamento 
    left join
        Cobranca c 
            ON c.avisoPagamento_id = l.id 
    where
        a.industria_id = ? 
        and a.varejo_id = ? 
        and a.numeroAvisoPagamento = ? 
        and a.dataAvisoPagamento between ? and ? 
    group by
        a.id,
        a.numeroAvisoPagamento,
        a.numeroAvisoPagamento,
        a.industria_id,
        a.varejo_id,
        a.dataAvisoPagamento,
        a.statusAvisoPagamento,
        a.dataUploadArquivo,
        a.dataImportacaoArquivo,
        a.dataConciliacaoAviso

模型

AvisoPagamento

@Entity(name = "AvisoPagamento")
public class AvisoPagamento {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@OneToMany(mappedBy = "avisoPagamento", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<CobrancaAvisoPagamento> cobrancas;

@OneToMany(mappedBy = "avisoPagamento", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@OrderBy("dataAcao ASC")
@JsonIgnore(accept={"AvisoPagamentoController.*"})
private List<LogAvisoPagamento> logAvisoPagamento;
}

LoteAvisoPagamento

@Entity(name = "LoteAvisoPagamento")
public class LoteAvisoPagamento {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@Column(nullable = false)
private Long codigoAviso;

}

Cobranca

public class Cobranca {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinColumn(name = "avisoPagamento_id")
@JsonIgnore(accept = { "CobrancaLoteController.listaCobrancas", "CobrancaAdmController.*",
        "ConciliacaoController.*", "CobrancaIndController.*" })
private LoteAvisoPagamento avisoPagamento;
}

我有两个问题:

  1. 实体LoteAvisoPagamento和AvisoPagamento,他们之间没有关系所以我强迫&#34;联合两栏:ON LoteAvisoPagamento.codigoAviso = AvisoPagamento.numeroAvisoPagamento。适用于SQL Native。
  2. 我需要计算Table Cobranca的记录,所以我用了count(c.avisoPagamento_id)
  3. 我想将这个SQL重新编写为TypedQuery和CriteriaBuilder,所以我尝试了这个:

    //Create Criteria Builder
            final CriteriaBuilder builder = manager.getCriteriaBuilder();
            //Create CriteriaQuery da Classe AvisoPagamento
            final CriteriaQuery<AvisoPagamento> query = builder.createQuery(AvisoPagamento.class);
            //Create from
            final Root<AvisoPagamento> rootAviso = query.from(AvisoPagamento.class);
    
            //Left Join Lote Aviso Pagamento
            Root<LoteAvisoPagamento> rootLoteAviso = query.from(LoteAvisoPagamento.class);
    
            final Predicate predicateLeftJoin = builder.equal(rootAviso.get("numeroAvisoPagamento"), rootLoteAviso.get("codigoAviso"));
    
            //Conditions
            Predicate predicateAvisoPagamento = builder.and();
    
            //Join Selects
            Predicate criteria = builder.conjunction();
            criteria = builder.and(criteria, predicateAvisoPagamento);
            criteria = builder.and(criteria, predicateLeftJoin);
    
            //Passou a Industria
            if (industria != null){
                 predicateAvisoPagamento = builder.and(predicateAvisoPagamento, builder.equal(rootAviso.get("industria"), industria));
            }
            //Passou o Varejo
            if (varejo != null){
                predicateAvisoPagamento = builder.and(predicateAvisoPagamento, builder.equal(rootAviso.get("varejo"), varejo));
            }
            //Passou o numero do Aviso
            if (numeroAviso != null){
                predicateAvisoPagamento = builder.and(predicateAvisoPagamento, builder.equal(rootAviso.get("numeroAvisoPagamento"), numeroAviso));
            }
    
            //Passou as Datas De e Ate
            if (dataDe != null && dataAte != null){
                predicateAvisoPagamento = builder.between(rootAviso.<Date>get("dataAvisoPagamento"), dataDe , dataAte);
            }
    
    
            //TypedQuery eh mais robusto, a checagem de tipo é feito na compilacao, eliminando alguns
            //tipos de erros
            final TypedQuery<AvisoPagamento> typedQuery = manager.createQuery(
                query.select(rootAviso).distinct(true)
                .where( criteria )
                .orderBy(builder.desc(rootAviso.get("dataConciliacaoAviso")))
            );
    
    
            //return List
            final List<AvisoPagamento> results = typedQuery.getResultList();
    
            return results;
    

    然后JPA生成了这个SQL:

    select
            distinct avisopagam0_.id as id1_9_,
            avisopagam0_.arquivoFisico as arquivoF2_9_,
            avisopagam0_.dataAvisoPagamento as dataAvis3_9_,
            avisopagam0_.dataConciliacaoAviso as dataConc4_9_,
            avisopagam0_.dataImportacaoArquivo as dataImpo5_9_,
            avisopagam0_.dataUploadArquivo as dataUplo6_9_,
            avisopagam0_.industria_id as industri9_9_,
            avisopagam0_.numeroAvisoPagamento as numeroAv7_9_,
            avisopagam0_.statusAvisoPagamento as statusAv8_9_,
            avisopagam0_.usuario_id as usuario10_9_,
            avisopagam0_.varejo_id as varejo_11_9_ 
        from
            AvisoPagamento avisopagam0_ cross 
        join
            LoteAvisoPagamento loteavisop1_ 
        where
            1=1 
            and 1=1 
            and avisopagam0_.numeroAvisoPagamento=loteavisop1_.codigoAviso 
        order by dataAvisoPagamento desc
    

    如何使用TypedQuery计算Table Cobranca中的记录以及如何解决此问题:

     where
                1=1 
                and 1=1
    

    很奇怪,我已经阅读了很多关于TypedQuery的内容,但我已经被卡住了

2 个答案:

答案 0 :(得分:0)

我认为 ON 子句仅适用于JPA 2.1版本中的关系。

所以直到现在你都不能使用

联合两栏:ON LoteAvisoPagamento.codigoAviso = AvisoPagamento.numeroAvisoPagamento

因为JPA 2.1(上一版本)不支持。

因此它不适用于Criteria或JPQL

  

注意: CROSS JOIN 不需要 ON 子句,这就是为什么你在生成的查询中看到它也不能LEFT JOIN in   你用这种方式的标准(不可能)它总会如此   生成为CROSS JOIN

     

INNER JOIN和LEFT JOIN需要实体之间的关系

尝试下一个JPQL,并测试它是否有效(我认为它不起作用),它很简单但它应该与你想要的类似(至少类似)在一个条件,直到现在)

SELECT aviso.id, aviso.numeroAvisoPagamento, loteAviso.id
FROM AvisoPagamento aviso
LEFT JOIN LoteAvisoPagamento loteAviso 
ON loteAviso.codigoAviso = aviso.numeroAvisoPagamento
WHERE aviso.numeroAvisoPagamento = :numeroAviso

:numeroAviso 替换为任何有效值,然后将其测试为entityManager.createQuery(此处放置查询

无论如何,我测试了我的不同实体,但逻辑相同,我得到了一个例外,因为我预期

注意:我正在使用 JPA Hibernate 提供商

这是我得到的例外

  

引起:org.hibernate.hql.internal.ast.QuerySyntaxException:期望加入的路径!

所以它期待类似的东西(你的实体不支持)

  

LEFT JOIN aviso.loteAvisoPagamento loteAviso

在下一个查询中

SELECT aviso.id, aviso.numeroAvisoPagamento, loteAviso.id
FROM AvisoPagamento aviso
LEFT JOIN aviso.loteAvisoPagamento loteAviso
ON loteAviso.codigoAviso = aviso.numeroAvisoPagamento
WHERE aviso.numeroAvisoPagamento = :numeroAviso

答案 1 :(得分:0)

正如@ mibrahim.iti告诉LEFT JOIN或INNER需要实体之间的关系所以我使用Native SQL来做这个,这是我的最终解决方案:

//Sql Log
        final StringBuilder sqlLog = new StringBuilder();
        sqlLog.append("select l from LogAvisoPagamento l where l.avisoPagamento.id = :idAviso  order by l.dataAcao ");

        final Query queryLog = manager.createQuery(sqlLog.toString());

        //Sql Aviso
        final StringBuilder sql = new StringBuilder();
        sql.append("select a.id, ")
        .append(" a.numeroAvisoPagamento, ")
        .append(" a.industria_id, ")
        .append(" a.varejo_id, ")
        .append(" a.dataAvisoPagamento, ")
        .append(" a.statusAvisoPagamento, ")
        .append(" a.dataUploadArquivo, ")
        .append(" a.dataImportacaoArquivo, ")
        .append(" a.dataConciliacaoAviso, ")
        .append(" count(c.avisoPagamento_id) as qtdeNotas, ")
        .append(" r.valorTotalBruto ")
        .append(" from AvisoPagamento a ")
        .append(" left join ResumoAvisoPagamento r ON r.avisoPagamento_id = a.id ")
        .append(" left join LoteAvisoPagamento l ON l.codigoAviso = a.numeroAvisoPagamento")
        .append(" left join Cobranca c ON c.avisoPagamento_id = l.id");

        boolean where = false;

        //Passou a Industria
        if (industria != null){
            sql.append(" where a.industria_id = :idIndustria");
            where = true;
        }

        //Passou o Varejo
        if (varejo != null){
            if (!where){
                sql.append(" where a.varejo_id = :idVarejo");
                where = true;
            }else{
                sql.append(" and a.varejo_id = :idVarejo");
            }
        }

        //Passou o numero do Aviso
        if (numeroAviso != null){
            if (!where){
                sql.append(" where a.numeroAvisoPagamennto = :numeroAvisoPagamento");
                where = true;
            }else{
                sql.append(" and a.numeroAvisoPagamento = :numeroAvisoPagamento");
            }

        }

        //Passou as Datas De e Ate
        if (dataDe != null && dataAte != null){
            if (!where){
                where = true;
                sql.append(" where a.dataAvisoPagamento between :dataDe and :dataAte");
            }else{
                sql.append(" and a.dataAvisoPagamento between :dataDe and :dataAte");
            }
        }

        sql.append(" group by a.id, a.numeroAvisoPagamento, ")
        .append(" a.numeroAvisoPagamento, ")
        .append(" a.industria_id, ")
        .append(" a.varejo_id, ")
        .append(" a.dataAvisoPagamento, ")
        .append(" a.statusAvisoPagamento, ")
        .append(" a.dataUploadArquivo, ")
        .append(" a.dataImportacaoArquivo, ")
        .append(" a.dataConciliacaoAviso, ")
        .append(" r.valorTotalBruto ");

        final Query query = manager.createNativeQuery(sql.toString());

        //Passou a Industria
        if (industria != null){
            query.setParameter("idIndustria", industria.getId());
        }

        //Passou o Varejo
        if (varejo != null){
            query.setParameter("idVarejo", varejo.getId());
        }

        //Passou o numero do Aviso
        if (numeroAviso != null){
            query.setParameter("numeroAvisoPagamento", numeroAviso);
        }

        //Passou as Datas De e Ate
        if (dataDe != null && dataAte != null){
            query.setParameter("dataDe", dataDe);
            query.setParameter("dataAte", dataAte);
        }

        final List<AvisoPagamento> avisosPagamentos = new ArrayList<AvisoPagamento>();
        //Percorrendo os Registros
        for (final Object array : query.getResultList()){

            final Object[] arrayAviso = (Object[]) array;

            final ResumoAvisoPagamento resumo = new ResumoAvisoPagamento();
            resumo.setValorTotalBruto((BigDecimal) arrayAviso[10]);

            final AvisoPagamento avisoPagamento = new AvisoPagamento();

            final BigInteger id = new BigInteger(arrayAviso[0].toString());
            avisoPagamento.setId(id.longValue());
            avisoPagamento.setNumeroAvisoPagamento((String) arrayAviso[1]);
            avisoPagamento.setDataAvisoPagamento((Date) arrayAviso[4]);
            avisoPagamento.setStatusAvisoPagamento(StatusAvisoPagamento.valueOf(arrayAviso[5].toString()));
            avisoPagamento.setDataUploadArquivo((Date) arrayAviso[6]);
            avisoPagamento.setDataImportacaoArquivo((Date) arrayAviso[7]);
            avisoPagamento.setDataConciliacaoAviso((Date) arrayAviso[8]);

            final BigInteger qtde = new BigInteger(arrayAviso[9].toString());
            avisoPagamento.setQtdeNotas(qtde.intValue());

            queryLog.setParameter("idAviso", avisoPagamento.getId());
            //Get Log
            final List<LogAvisoPagamento> logs = queryLog.getResultList();

            avisoPagamento.setLogAvisoPagamento(logs);
            avisoPagamento.setResumoAvisoPagamento(resumo);
            avisosPagamentos.add(avisoPagamento);
        }

        return avisosPagamentos;