5
respostas

Erro na Conversão

Prezados, Modifiquei um pouco o código da aula objetivando melhorar o resultado da consulta. Todavia, a instrução sql "convert" não funciona, conforme código abaixo. Na classe "Movimentacao" o atributo é declarado desta forma "private BigDecimal valor;". O "avg" retorna um "Double", então tento a conversão. Alguma dica? Obrigado ...

public static void mediaDiaValorMovimentacaoConta2 (int numConta) {

        EntityManager em = new JPAUtil().getEntityManager();
        em.getTransaction().begin();

        Conta conta = em.find(Conta.class, numConta);

        String jpql = "select m.data, convert(avg(m.valor),decimal) from Movimentacao m where m.conta = :pConta" +
                " and m.tipo = :pTipo" +
                " group by date(m.data)";

        Query query = em.createQuery(jpql);

        query.setParameter("pConta", conta);
        query.setParameter("pTipo", TipoMovimentacao.SAIDA);

        List<Movimentacao> medias = query.getResultList();

        System.out.println("O cliente " + conta.getCliente().getNome() + " possui as seguintes médias de saída");
        for (Movimentacao md : medias) {
            System.out.println(md.getData() + " " + md.getValor());            
        }

        em.getTransaction().commit();
        em.close();
    }
5 respostas

Essa função convert é suportada pelo seu banco? vi na documentação do hibernate e não achei nada referente a ela na jpql... Caso o banco tenha suporte, o comportamento esperado era que o hibernate delegasse a chamada... Caso isso não esteja acontecendo, você pode tentar usar a função cast mesmo.. por aceitar o retorno como double, pode ser uma opção. Recebe como double e converte para bigdecimal dentro do objeto.

Alberto, o "convert" é suportado. Funciona quando executo a query direto no banco. Segue abaixo o erro na aplicação

Exception in thread "main" java.lang.IllegalArgumentException: org.hibernate.QueryException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode 
 \-[METHOD_CALL] MethodNode: '('
    +-[METHOD_NAME] IdentNode: 'convert' {originalText=convert}
    \-[EXPR_LIST] SqlNode: 'exprList'
       +-[AGGREGATE] AggregateNode: 'avg'
       |  \-[DOT] DotNode: 'movimentac0_.valor' {propertyName=valor,dereferenceType=PRIMITIVE,getPropertyPath=valor,path=m.valor,tableAlias=movimentac0_,className=br.com.caelum.financas.modelo.Movimentacao,classAlias=m}
       |     +-[ALIAS_REF] IdentNode: 'movimentac0_.id' {alias=m, className=br.com.caelum.financas.modelo.Movimentacao, tableAlias=movimentac0_}
       |     \-[IDENT] IdentNode: 'valor' {originalText=valor}
       \-[IDENT] IdentNode: 'decimal' {originalText=decimal}
 [select m.data, convert(avg(m.valor),decimal) from br.com.caelum.financas.modelo.Movimentacao m where m.conta = :pConta and m.tipo = :pTipo group by date(m.data)]
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:131)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162)
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:663)
    at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23)
    at br.com.caelum.financas.teste.TestaFuncoesJPQL.mediaDiaValorMovimentacaoConta2(TestaFuncoesJPQL.java:127)
    at br.com.caelum.financas.teste.TesteConta.main(TesteConta.java:51)
Caused by: org.hibernate.QueryException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode 
 \-[METHOD_CALL] MethodNode: '('
    +-[METHOD_NAME] IdentNode: 'convert' {originalText=convert}
    \-[EXPR_LIST] SqlNode: 'exprList'
       +-[AGGREGATE] AggregateNode: 'avg'
       |  \-[DOT] DotNode: 'movimentac0_.valor' {propertyName=valor,dereferenceType=PRIMITIVE,getPropertyPath=valor,path=m.valor,tableAlias=movimentac0_,className=br.com.caelum.financas.modelo.Movimentacao,classAlias=m}
       |     +-[ALIAS_REF] IdentNode: 'movimentac0_.id' {alias=m, className=br.com.caelum.financas.modelo.Movimentacao, tableAlias=movimentac0_}
       |     \-[IDENT] IdentNode: 'valor' {originalText=valor}
       \-[IDENT] IdentNode: 'decimal' {originalText=decimal}
 [select m.data, convert(avg(m.valor),decimal) from br.com.caelum.financas.modelo.Movimentacao m where m.conta = :pConta and m.tipo = :pTipo group by date(m.data)]
    at org.hibernate.QueryException.generateQueryException(QueryException.java:120)
    at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:103)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:217)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
    at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
    at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:546)
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:655)

Modifiquei a query utilizando o cast. No banco funcionou. Na aplicação também não resolveu. Segue abaixo a modificação e o erro.

String jpql = "select m.data, cast(avg(m.valor) as decimal) from Movimentacao m where m.conta = :pConta" +
                " and m.tipo = :pTipo" +
                " group by date(m.data)";

********************************************

Exception in thread "main" java.lang.IllegalArgumentException: org.hibernate.QueryException: Could not resolve requested type for CAST : decimal [select m.data, cast(avg(m.valor) as decimal) from br.com.caelum.financas.modelo.Movimentacao m where m.conta = :pConta and m.tipo = :pTipo group by date(m.data)]
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:131)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162)
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:663)
    at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23)
    at br.com.caelum.financas.teste.TestaFuncoesJPQL.mediaDiaValorMovimentacaoConta2(TestaFuncoesJPQL.java:127)
    at br.com.caelum.financas.teste.TesteConta.main(TesteConta.java:51)
Caused by: org.hibernate.QueryException: Could not resolve requested type for CAST : decimal [select m.data, cast(avg(m.valor) as decimal) from br.com.caelum.financas.modelo.Movimentacao m where m.conta = :pConta and m.tipo = :pTipo group by date(m.data)]

Caso o hibernate não funcione com esperado, como realizar esta conversão uma vez que o resultado é uma lista com 2 parâmetros, ou seja, uma lista de campos data e médias ??

Opa, nesse caso, o melhor é você uma feature da jpa que chama constructor expression => http://docs.oracle.com/html/E13946_04/ejb3_langref.html#ejb3_langref_constructor

Basicamente você vai criar uma classe que não é entidade, criar atributos que representem as informações do select e, lá dentro, você pode fazer suas conversoes.

Show Alberto, quando usei o constructor expression funcionou!

Porém, quando tentei fazer um foreach "for (ClasseEntidade movimentacao : NovaClasseNãoEntidade)" gerou a seguinte exception

java.lang.ClassCastException: br.com.caelum.financas.DAO.MovimentacaoInfo cannot be cast to br.com.caelum.financas.modelo.Movimentacao

Alguma dica para resolver esta exception ??