JPA - problemas de paginacao com setMaxResults

7 respostas
D
Boa tarde galera... eu estou iniciando agora com JPA a comecei a enfrentar alguns problemas.. possuo uma tabela com uns 30mil registros e preciso ler um a um e fazer algum tipo de processamento... segue abaixo o codigo:
public class ClasseXXX
{
    private static final Logger LOG = Logger.getLogger( "logger" );
    
    private static final Integer FETCH_SIZE = 1000;
    
    private EntityManager em;
    
    public void executar( )
    {
        List<Object> list;
        Query query;
        int i = 0;
        
        list = null;

        LOG.log( Level.INFO, "Inicializando o processamento dos registros..." );
        
        
        while( list == null || list.size( ) > 0 )
        {
            LOG.log( Level.INFO, "Listando os próximos " + FETCH_SIZE + " registros." );

            query = em.createNamedQuery( "ClassePOJO.listar" );
            query.setMaxResults( FETCH_SIZE );
            query.setFirstResult( i * FETCH_SIZE );
            list = query.getResultList( );
            
            LOG.log( Level.INFO, list.size( ) + " registro(s) encontrados." );
            
            for( Object obj : list )
                processaRegistro( obj );
            
            i++;
        }
        
        LOG.log( Level.INFO, "Conversão dos registros executada com sucesso!" );
    }
    
    public EntityManager getEntityManager( )
    { return em; }
    
    public void setEntityManager( EntityManager em )
    { this.em = em; }
    
    public static void main( String[ ] args ) throws Exception
    {
        // Create the EntityManager
        EntityManagerFactory factory = Persistence.createEntityManagerFactory( "cadsus" );
        EntityManager em = factory.createEntityManager( );
        
        ClasseXXX instancia = new ClasseXXX( );
        em.getTransaction( ).begin( );
        instancia.setEntityManager( em );
        instancia.executar( );
        em.getTransaction( ).commit( );
        em.close( );
    }

    private void processaRegistro( Object )
    {
        //processa alguma coisa
    }
    
}
algumas observacoes: 1 - o trecho de codigo
query = em.createNamedQuery( "ClassePOJO.listar" );
            query.setMaxResults( FETCH_SIZE );
            query.setFirstResult( i * FETCH_SIZE );
            list = query.getResultList( );
foi escrito desta forma pois fazendo o createNamedQuery e setMaxResults fora do loop, ele listava todos os registros. pq? sei lah... existe forma de fazer isso? 2 - a transacao foi ativada numa tentativa frustrada de dar em.flush()

Os problemas:

1 - apos processar qse 5 mil registros, ocorre um OutOfMemory... alguem sabe me explicar pq?

2 - a coisa mais bizarra do mundo: algumas vezes o getResultList retorna mais de 1000 registros... o.O
run-single:
[TopLink Info]: 2007.08.07 10:11:24.525--ServerSession(23491286)--TopLink, version: Oracle TopLink Essentials - 2.0 (Build b41-beta2 (03/30/2007))
[TopLink Info]: 2007.08.07 10:11:24.868--Not able to detect platform for vendor name [Firebird]. Defaulting to [oracle.toplink.essentials.platform.database.DatabasePlatform]. The database dialect used may not match with the database you are using. Please explicitly provide a platform using property toplink.platform.class.name.
[TopLink Info]: 2007.08.07 10:11:25.493--ServerSession(23491286)--file:/E:/Projetos/Programa/build/classes/-database login successful
07/08/2007 10:11:25 programa.ClasseXXX executar
INFO: Inicializando a conversão dos registros...
07/08/2007 10:11:25 programa.ClasseXXX executar
INFO: Listando os próximos 1000 registros.
07/08/2007 10:11:58 programa.ClasseXXX executar
INFO: 1200 registro(s) encontrados.
07/08/2007 10:11:58 programa.ClasseXXX executar
INFO: Listando os próximos 1000 registros.
07/08/2007 10:12:30 programa.ClasseXXX executar
INFO: 1000 registro(s) encontrados.
07/08/2007 10:12:31 programa.ClasseXXX executar
INFO: Listando os próximos 1000 registros.
07/08/2007 10:13:08 programa.ClasseXXX executar
INFO: 1200 registro(s) encontrados.
07/08/2007 10:13:09 programa.ClasseXXX executar
INFO: Listando os próximos 1000 registros.
07/08/2007 10:13:33 programa.ClasseXXX executar
INFO: 1000 registro(s) encontrados.
07/08/2007 10:13:40 programa.ClasseXXX executar
INFO: Listando os próximos 1000 registros.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at org.firebirdsql.gds.XdrInputStream.readSQLData(XdrInputStream.java:89)
[TopLink Info]: 2007.08.07 10:13:57.650--ServerSession(23491286)--file:/E:/Projetos/ADI/ADI_Conversao/CVR_CadSUS/build/classes/-cadsus logout successful
        at org.firebirdsql.jgds.GDS_Impl.isc_dsql_fetch(GDS_Impl.java:1093)
        at org.firebirdsql.jca.FBManagedConnection.fetch(FBManagedConnection.java:828)
        at org.firebirdsql.jdbc.AbstractConnection.fetch(AbstractConnection.java:969)
        at org.firebirdsql.jdbc.FBCachedFetcher.<init>(FBCachedFetcher.java:63)
        at org.firebirdsql.jdbc.FBResultSet.<init>(FBResultSet.java:113)
        at org.firebirdsql.jdbc.AbstractStatement.getCachedResultSet(AbstractStatement.java:568)
        at org.firebirdsql.jdbc.AbstractPreparedStatement.executeQuery(AbstractPreparedStatement.java:115)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeSelect(DatabaseAccessor.java:711)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:486)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:437)
        at oracle.toplink.essentials.threetier.ServerSession.executeCall(ServerSession.java:465)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:213)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:199)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeSelectCall(DatasourceCallQueryMechanism.java:270)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.selectAllRows(DatasourceCallQueryMechanism.java:600)
        at oracle.toplink.essentials.internal.queryframework.ExpressionQueryMechanism.selectAllRowsFromTable(ExpressionQueryMechanism.java:2240)
        at oracle.toplink.essentials.internal.queryframework.ExpressionQueryMechanism.selectAllReportQueryRows(ExpressionQueryMechanism.java:2206)
        at oracle.toplink.essentials.queryframework.ReportQuery.executeDatabaseQuery(ReportQuery.java:774)
        at oracle.toplink.essentials.queryframework.DatabaseQuery.execute(DatabaseQuery.java:609)
        at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:677)
        at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:731)
        at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2219)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:937)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:909)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:346)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.getResultList(EJBQueryImpl.java:453)
        at programa.ClasseXXX.executar(ConversaoPessoas.java:98)
        at programa.ClasseXXX.main(ConversaoPessoas.java:139)
Java Result: 1
EXECUTADO COM SUCESSO (tempo total: 2 minutos 43 segundos)

Estou utilizando o netbeans 5.5.1 e o Oracle TopLink Essentials - 2.0 (Build b41-beta2)... estou usando o banco de dados firebird 1.5.4 e o driver jdbc JayBird JCA/JDBC 1.5.5...
Alguem poderia me explicar o pq desses problemas, como corrigir ou se existe alguma outra forma de listar tds os registros de uma tabela sem dar OutOfMemory? Isto é problema da jpa ou do driver JDBC?

vlws...

7 Respostas

L

Eu acho que seu problema (de OutOfMemory) esta na transação, tudo esta sendo feito numa unica transação… isso quer dizer que nada vai ser comitado no banco e tudo que vc “processar” vai ficando em memoria.
Explicando melhor, ele vai carregando os registros (com paginação), vc processa ele, salva, mas não comita a transação, então o objeto ficou na session, e ai vai indo, agrupando tudo até que falta memoria…

não tem como vc processar em partes?! tem que ser tudo feito dentro da mesma transação?

e se vc listar apenas, não processar os registros, tira transação fora e somente listar, da erro?!

D

observe q a transacao esta fora do codigo do processamento... esta no bloco de codigo q cria a sessao jpa... isso pq testei das duas formas... possuo um codigo mais enxuto q apenas percorre todos os registros da tabela, utilizando a msm namedQuery do outro metodo:

public void listar( )
    {
        List<Object> list;
        Query query;
        int i;
        
        list = null;
        i    = 0;
        
        while( list == null || list.size( ) > 0 )
        {
            query = em.createNamedQuery( "ClassePOJO.listar" );
            
            query.setMaxResults( FETCH_SIZE );
            query.setFirstResult( i * FETCH_SIZE );

            list = query.getResultList( );

            for( Object obj : list );

            i++;
        }
    }

ocorre o msm erro:

run-single:
[TopLink Info]: 2007.08.07 02:23:40.586--ServerSession(30191657)--TopLink, version: Oracle TopLink Essentials - 2.0 (Build b41-beta2 (03/30/2007))
[TopLink Info]: 2007.08.07 02:23:42.101--Not able to detect platform for vendor name [Firebird]. Defaulting to [oracle.toplink.essentials.platform.database.DatabasePlatform]. The database dialect used may not match with the database you are using. Please explicitly provide a platform using property toplink.platform.class.name.
[TopLink Info]: 2007.08.07 02:23:42.820--ServerSession(30191657)--file:/E:/.../-database login successful
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at org.firebirdsql.encodings.Encoding_OneByte.<init>(Encoding_OneByte.java:68)
        at org.firebirdsql.encodings.Encoding_Cp1252.<init>(Encoding_Cp1252.java:53)
        at org.firebirdsql.encodings.EncodingFactory.createEncoding(EncodingFactory.java:125)
[TopLink Info]: 2007.08.07 02:28:51.116--ServerSession(30191657)--file:/E:/.../-database logout successful
        at org.firebirdsql.encodings.EncodingFactory.getEncoding(EncodingFactory.java:114)
        at org.firebirdsql.encodings.EncodingFactory.getEncoding(EncodingFactory.java:88)
        at org.firebirdsql.gds.XSQLVAR.decodeString(XSQLVAR.java:230)
        at org.firebirdsql.jdbc.field.FBStringField.getString(FBStringField.java:158)
        at org.firebirdsql.jdbc.field.FBWorkaroundStringField.getString(FBWorkaroundStringField.java:118)
        at org.firebirdsql.jdbc.FBResultSet.getString(FBResultSet.java:345)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.getObjectThroughOptimizedDataConversion(DatabaseAccessor.java:975)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.getObject(DatabaseAccessor.java:904)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.fetchRow(DatabaseAccessor.java:746)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:510)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:437)
        at oracle.toplink.essentials.threetier.ServerSession.executeCall(ServerSession.java:465)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:213)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:199)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.selectOneRow(DatasourceCallQueryMechanism.java:620)
        at oracle.toplink.essentials.internal.queryframework.ExpressionQueryMechanism.selectOneRowFromTable(ExpressionQueryMechanism.java:2277)
        at oracle.toplink.essentials.internal.queryframework.ExpressionQueryMechanism.selectOneRow(ExpressionQueryMechanism.java:2252)
        at oracle.toplink.essentials.queryframework.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:350)
        at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:709)
        at oracle.toplink.essentials.queryframework.DatabaseQuery.execute(DatabaseQuery.java:609)
        at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:677)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.internalExecuteQuery(AbstractSession.java:1815)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:937)
        at oracle.toplink.essentials.internal.indirection.NoIndirectionPolicy.valueFromQuery(NoIndirectionPolicy.java:235)
        at oracle.toplink.essentials.mappings.ForeignReferenceMapping.valueFromRowInternal(ForeignReferenceMapping.java:953)
        at oracle.toplink.essentials.mappings.OneToOneMapping.valueFromRowInternal(OneToOneMapping.java:1050)
        at oracle.toplink.essentials.mappings.ForeignReferenceMapping.valueFromRow(ForeignReferenceMapping.java:890)
        at oracle.toplink.essentials.mappings.DatabaseMapping.readFromRowIntoObject(DatabaseMapping.java:1007)
        at oracle.toplink.essentials.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:266)
Java Result: 1
EXECUTADO COM SUCESSO (tempo total: 5 minutos 33 segundos)

ps: este output foi gerado sem a utilizacao de transacao.

L

tah e seu bloco de processamento é chamado no bloco que criou a transação, e vc usa para fazer as consultas o EntityManager que foi usado para iniciar a transação, logo, sim, seu processamento esta dentro de uma transação.

Bem, mas se vc testou sem transação e tb deu erro… vou fazer um teste igual o seu quando chegar em casa (to sem tempo agora no trabalho), ai te respondo… mas eu ja fiz consultas em tabelas com 200 mil registros usando JPA com paginação e deu certo… deve ser algum detalhe que não vimos ainda… vou testar…

ps: a implementação que usei na epoca era Hibernate

L

Não sei, testei aqui o mesmo programa que o seu, acessando uma tabela com 5 colunas e 39 mil registros e não deu erro.

Só que no meu caso usei hibernate e mysql, to meio sem tempo e saco de baixar o toplink e testar com ele.

Sera que essa sua “ClassePOJO” não tem muitos mapeamentos EAGER para Set que esta carregando demais?!

Posta essa classe para eu ver mais ou menos… e faz uns testes iniciando a paginacao la pelo 10000 ± (i=100)… da uma olhada se dessa forma consegue listar mais registros.

flw

D

eu fiz a coisa mais absurda e ridicula do mundo em um momento de desespero... kkk.. mas funcionou.. claro q eu nao to afim de deixar o codigo assim... eu acho q o problema eh o driver JDBC do firebird... mas vou postar a alteracao grotestaca q fiz.. kkkkk

public void executar( ) throws Exception
    {
        EntityManagerFactory factory;
        List<Object> list;
        Query query;
        int i = 0;
        
        list = null;
        
        LOG.log( Level.INFO, "Inicializando o processamento dos registros..." );
        
        while( list == null || list.size( ) > 0 )
        {
            LOG.log( Level.INFO, "Listando os próximos " + FETCH_SIZE + " registros." );
            
            /****** POG ******/
            // Create the EntityManager
            factory = Persistence.createEntityManagerFactory( "database" );
            this.em = factory.createEntityManager( );
            /****** POG ******/
            
            query = em.createNamedQuery( "ClassePOJO.listar" );
            query.setMaxResults( FETCH_SIZE );
            query.setFirstResult( i * FETCH_SIZE );
            list = query.getResultList( );
            
            LOG.log( Level.INFO, list.size( ) + " registro(s) encontrados." );
            
            for( Object obj : list )
            {
                //processa....
            }
            
            /****** POG ******/
            this.em.close( );
            /****** POG ******/
            
            i++;
        }
        
        LOG.log( Level.INFO, "Processamento dos registros executada com sucesso!" );
    }

o pior.. esse lixo funcionou.. kkk.. processou meus 30mil registros...

qnto ao pojo ele possui mtos relacionamentos EAGER msm.. vou modificar pra LAZY e ver se melhora...

vlws...

D

observei os mapeamentos ake… mas a grande maioria dos relacionamentos eu utilizo no processamento das informacoes… e agora? :S

D

resolvi ake \o/

fiz algumas alteracoes ake q contribuiram… foram elas:

  • trokei o driver JDBC para uma versao classe 4 - nao fez diferenca nenhuma aparentemente… kkk

  • trokei o toplink pelo hibernate - uma grande diferenca… o toplink processava apenas uns 5mil registros antes de dar OutOfMemory… o hibernate processou 12mil… apesar dos mapeamentos terem dado mto pau… kkk(nao era pra funcionar nos dois?? o.O)

  • e por ultimo, e mais importante, enfiei um em.clear() lah no meio do codigo… ficou lindo… e da forma como eu qria…

de qualquer forma obrigado pela ajuda luBS…

vlws

Criado 7 de agosto de 2007
Ultima resposta 9 de ago. de 2007
Respostas 7
Participantes 2