Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

InstantiationException ao rodar JPQL com SELECT new

Olá, sou iniciante no mundo Java e estou acompanhando as atividades "Java e JPA: Consultas avançadas, performance e modelos complexos" com o Rodrigo Ferreira.

Estou usando um banco real Postgres ao invés do H2 usado no curso. E, no momento, estou obtendo uma org.hibernate.query.sqm.sql.internal.InstantiationException ao executar uma instrução JPQL com um Select new e não estou encontrando respostas.

Pelo que entendi da exceção disparada, tem a ver com a Instanciação automática executada pelo Hibernate, dá a impressão de que ele não consegue instanciar o List que deve ser retornado pelo método que faz o select new. Segue a descrição do erro lançada no console:

Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.query.sqm.sql.internal.InstantiationException: Error performing dynamic instantiation : br.com.jpaexample.shop.vo.SalesReportVo
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:166)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:193)
    at org.hibernate.query.spi.AbstractQuery.list(AbstractQuery.java:1373)
    at org.hibernate.query.Query.getResultList(Query.java:131)
    at br.com.jpaexample.shop.dao.CustomerOrderDao.getSalesReport(CustomerOrderDao.java:74)
    at br.com.jpaexample.shop.tests.VoTest.main(VoTest.java:20)
Caused by: org.hibernate.query.sqm.sql.internal.InstantiationException: Error performing dynamic instantiation : br.com.jpaexample.shop.vo.SalesReportVo
    at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationAssemblerConstructorImpl.assemble(DynamicInstantiationAssemblerConstructorImpl.java:58)
    at org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:93)
    at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:66)
    at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:24)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:228)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:146)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:74)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:80)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:248)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:191)
    at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:513)
    at org.hibernate.query.spi.AbstractQuery.list(AbstractQuery.java:1364)
    ... 3 more

Segue o código da minha classe VO:

package br.com.jpaexample.shop.vo;

import java.time.LocalDate;

public class SalesReportVo {

    private String productName;
    private Long quantitySold;
    private LocalDate lastSaleDate;

    public SalesReportVo() {}

    public SalesReportVo(String productName, Long quantitySold, LocalDate lastSaleDate) {
        this.productName = productName;
        this.quantitySold = quantitySold;
        this.lastSaleDate = lastSaleDate;
    }

       /*Getters*/

    @Override
    public String toString() {
        return "SalesReportVo [productName=" + productName + ", quantitySold=" + quantitySold + ", lastSaleDate="
                + lastSaleDate + "]";
    }
}

Segue o código do método onde executo a instrução JPQL:

    public List<SalesReportVo> getSalesReport() {
        String jpql = "SELECT new br.com.jpaexample.shop.vo.SalesReportVo( "
                + "product.name, "
                + "SUM(item.quantity), "
                + "MAX(t_order.dateOrder)) "
                + "FROM CustomerOrder t_order "
                + "JOIN t_order.items item "
                + "JOIN item.product product "
                + "GROUP BY product.name, "
                + "         item.quantity "
                + "ORDER BY item.quantity DESC ";
        return em.createQuery(jpql, SalesReportVo.class)
                .getResultList();
    }

E por último a classe de teste do DAO e VO:

package br.com.jpaexample.shop.tests;

import java.util.List;
import javax.persistence.EntityManager;
import br.com.jpaexample.shop.dao.CustomerOrderDao;
import br.com.jpaexample.shop.util.JpaUtil;
import br.com.jpaexample.shop.vo.SalesReportVo;

public class VoTest {
    public static void main(String[] args) {

        EntityManager em = JpaUtil.getEntityManager();
        CustomerOrderDao orderDao = new CustomerOrderDao(em);

        em.getTransaction().begin();

        List<SalesReportVo> reports = orderDao.getSalesReport();
        reports.forEach(System.out::println);        

        em.close();
    }
}

Alguma boa alma consegue dar aquele help esperto aí ?

4 respostas

Oi Vinicius,

Aparentemente não vi problemas no seu código.

Consegue postar aqui a exception completa?

Olá Rodrigo, sim claro.

Segue a stack trace completa retornada pelo Eclipse.

javax.persistence.PersistenceException: org.hibernate.query.sqm.sql.internal.InstantiationException: Error performing dynamic instantiation : br.com.jpaexample.shop.vo.SalesReportVo
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:166)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:193)
    at org.hibernate.query.spi.AbstractQuery.list(AbstractQuery.java:1373)
    at org.hibernate.query.Query.getResultList(Query.java:131)
    at br.com.jpaexample.shop.dao.CustomerOrderDao.getSalesReport(CustomerOrderDao.java:60)
    at br.com.jpaexample.shop.tests.VoTest.main(VoTest.java:21)
Caused by: org.hibernate.query.sqm.sql.internal.InstantiationException: Error performing dynamic instantiation : br.com.jpaexample.shop.vo.SalesReportVo
    at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationAssemblerConstructorImpl.assemble(DynamicInstantiationAssemblerConstructorImpl.java:58)
    at org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:93)
    at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:66)
    at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:24)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:228)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:146)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:74)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:80)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:248)
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:191)
    at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:513)
    at org.hibernate.query.spi.AbstractQuery.list(AbstractQuery.java:1364)
    ... 3 more
Caused by: java.lang.IllegalArgumentException: argument type mismatch
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
    at org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiationAssemblerConstructorImpl.assemble(DynamicInstantiationAssemblerConstructorImpl.java:52)
    ... 14 more

Não sei se ajuda, mas eu debuguei o processo e percebi que o erro ocorre na classe Classe DynamicInstantiationAssemblerConstructorImpl do pacote package org.hibernate.sql.results.graph.instantiation.internal:

    @Override
    public R assemble(
            RowProcessingState rowProcessingState,
            JdbcValuesSourceProcessingOptions options) {
        final int numberOfArgs = argumentReaders.size();
        Object[] args = new Object[ numberOfArgs ];
        for ( int i = 0; i < numberOfArgs; i++ ) {
            args[i] = argumentReaders.get( i ).assemble( rowProcessingState, options );
        }

        try {
            return targetConstructor.newInstance( args );
        }
        catch (InvocationTargetException e) {
            throw new InstantiationException( "Error performing dynamic instantiation : " + targetConstructor.getDeclaringClass().getName(), e.getCause() );
        }
        catch (Exception e) {
            throw new InstantiationException( "Error performing dynamic instantiation : " + targetConstructor.getDeclaringClass().getName(), e );
        }
    }

Aí chama o targetConstructor.newInstance...

    @CallerSensitive
    @ForceInline // to ensure Reflection.getCallerClass optimization
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        Class<?> caller = override ? null : Reflection.getCallerClass();
        return newInstanceWithCaller(initargs, !override, caller);
    }

Quando esse método acima devolve o newInstance, aí volta com essa Exception:

 (java.lang.IllegalArgumentException) java.lang.IllegalArgumentException: argument type mismatch
solução!

O problema foi esse então: argument type mismatch

Parece que essa consulta JPQL não está devolvendo os mesmos tipos de atributos que definidos no construtor do seu VO.

Para verificar, faça o seguinte teste e veja quais são os tipos que estão chegando:

public List<SalesReportVo> getSalesReport() {
        String jpql = "SELECT "
                + "product.name, "
                + "SUM(item.quantity), "
                + "MAX(t_order.dateOrder) "
                + "FROM CustomerOrder t_order "
                + "JOIN t_order.items item "
                + "JOIN item.product product "
                + "GROUP BY product.name, "
                + "item.quantity "
                + "ORDER BY item.quantity DESC ";

        List<Object[]> resultado = em.createQuery(jpql).getResultList();
    resultado.foreach(r -> {
        System.out.println(r[0]);
        System.out.println(r[1]);
        System.out.println(r[2]);
    });
    return null;
    }

Exatamente Rodrigo,

Não me liguei que no banco postgres eu tenho o field quantity como sendo um integer. E na classe VO defini como Long. Na minha cabeça, eu tinha que o integer, por ser supostamente menor, caberia no Long do Java. Bastou alterar na VO para int que funcionou perfeitamente.

Muito obrigado Mestre.