Java Hibernate após um tempo fica muito lento a inserção

9 respostas
G

Galera estou com um problema, um exemplo tenho 1000 registros para adicionar em algumas tabelas, onde dentro desse processo 7 movimentação no banco, 4 select e 3 inserts.
Depois de um tempo mais ou menos quando já inseriu 500 registros, o processo fica extremamente lento, inserindo um por vez. Podem me ajudar?
Sempre que consulto ou realizado a inserção eu utilizo o comando ClienteJpaDAO.getInstance()
.CLASSEQUEVOUFAZEROPERACAOABAIXO

public class ClienteJpaDAO {

private static ClienteJpaDAO instance;
 protected EntityManager entityManager;
 
 public static ClienteJpaDAO getInstance(){
           if (instance == null){
                    instance = new ClienteJpaDAO();
           }
           
           return instance;
 }

 private ClienteJpaDAO() {
           entityManager = getEntityManager();
 }

 private EntityManager getEntityManager() {
           EntityManagerFactory factory = Persistence.createEntityManagerFactory("crudHibernatePU");
           if (entityManager == null) {
                    entityManager = factory.createEntityManager();
           }
           return entityManager;
 }       

 public Object persist(Object cliente) {
           try {
                    entityManager.getTransaction().begin();
                    entityManager.persist(cliente);
                    entityManager.getTransaction().commit();
           } catch (Exception ex) {
                    ex.printStackTrace();
                    entityManager.getTransaction().rollback();
           }
           return cliente;
 }

 public tabelab merge(tabelab cliente) {
	Object toreturn = null;
           try {
                    entityManager.getTransaction().begin();
                    toreturn =   entityManager.merge(cliente);
                    entityManager.getTransaction().commit();
           } catch (Exception ex) {
                    ex.printStackTrace();
                    entityManager.getTransaction().rollback();
           }
           return (tabelab) toreturn;
 }

 public void merge3(tabelac cliente) {
     try {
              entityManager.getTransaction().begin();
              entityManager.merge(cliente);
              entityManager.getTransaction().commit();
     } catch (Exception ex) {
              ex.printStackTrace();
              entityManager.getTransaction().rollback();
     }
 }     


 public tabelab validarLogin(String codigo){
	 tabelab cliente = new tabelab();      
     try{
         Query query =  entityManager.createQuery("select a from tabelab as a "+
                   "where a.codigo = :paramFinalizado");
             query.setParameter("paramFinalizado", codigo);
             cliente = (tabelab) query.getSingleResult();
             //caso nao exista BD nao retorna null
           
    } catch(Exception ex){
    	 return cliente;
    }        
     return cliente;
 }
 

 public int consultarcat(String nome_exibicao){
	int valor = 0;
   //  EntityManager em = getEntityManager();
     try{
         Query query =  entityManager.createQuery("select id from item_cat as a "+
                   "where a.nome_exibicao = :paramFinalizado");
             query.setParameter("paramFinalizado", nome_exibicao);
             valor = (int) query.getSingleResult();
             //caso nao exista BD nao retorna null                   
    }
     catch(Exception ex){
    	 return 0;
     }        
     return valor;
 }
 
 public String validarDadosTmp(String codigo, String uf, int mes, int ano, int desoneradoOuN) { 		

	 String codigo1 = null;
		try {
			Query query = entityManager.createQuery("select a.codigo from tabelab as a" +
					" where a.codigo = :paramFinalizado" +
					" and a.unidade_federativa = :paramFinalizado2" +
					" and a.mes = :paramFinalizado3" +
					" and a.ano = :paramFinalizado4" +
					" and a.desonerado = :paramFinalizado5");
			query.setParameter("paramFinalizado", codigo);
			query.setParameter("paramFinalizado2", uf);
			query.setParameter("paramFinalizado3", mes);
			query.setParameter("paramFinalizado4", ano);
			query.setParameter("paramFinalizado5", desoneradoOuN);
			codigo1 =  (String) query.getSingleResult();
		// caso nao exista BD nao retorna null
		} catch (Exception ex) {
			return codigo1;
		} finally{
			//instance.
		}
		return codigo1;
 }
 
 public tabelaz validar(String codigo, int mes, int ano, int idUf) {
	 tabelaz teste = new tabelaz();
		try {
			Query query = entityManager.createQuery("select t2 from tabelau as t1, tabelaz as t2" +
					" where t1.id = t2.id_insumo" +
					" and t1.codigo = :paramFinalizado" +
					" and t2.mes = :paramFinalizado2" +
					" and t2.ano = :paramFinalizado3" +
					" and t2.id_uf = :paramFinalizado4");
			query.setParameter("paramFinalizado", codigo);
			query.setParameter("paramFinalizado2", mes);
			query.setParameter("paramFinalizado3", ano);
			query.setParameter("paramFinalizado4", idUf);
			teste= (tabelaz) query.getSingleResult();
			// caso nao exista BD nao retorna null
			} catch (Exception ex) {				
				return teste;
			}
		return teste;
 }}

9 Respostas

javaflex

Hibernate nunca foi recomendado para lidar com massa de dados, pois é uma ferramenta custosa. Use diretamente JDBC + SQL.

G

Nossa agora fiquei triste, já vi uma grande quantidade de pessoas usando inclusive com spring, então terei que desfazer essa ai=(

javaflex

Tem como amenizar o problema sem deixar de usar Hibernate. Ao invés de usar Session normal com cache, use StatelessSession. Só que vai ter que usar Hibernate puro sem as garras do JPA, pois pelo que já falaram aqui JPA não permite isso.

A

Na verdade, as coisas nao sao tao simples assim.

Mesmo que o Hibernate nao fosse recomendado para grandes volumes de dados (gostaria de ver fontes sobre isso), 1000 registros nao é nada. Você pode aumentar sua carga em algumas ordens de grandeza aí, e ainda seria de boa.

Deve ter algo errado no seu código, estar faltando fechar alguma coisa, etc.

Eu nao vi o loop que você faz os 1000 inserts no código que postou.

O Hibernate nao é a ferramenta mais simples de usar, mas tem um monte de gente usando e funciona para elas, nao precisa sair correndo e jogar tudo fora.

javaflex

Não é preciso pesquisar muito para achar fontes, e é muito fácil você mesmo testar. Quanto mais complexo for o processamento de uma grande massa de dados pior vai ser a performance. Segue um exemplo que achei:


http://phpdao.com/hibernate_vs_jdbc/
http://www.mindfiresolutions.com/mindfire/Java_Hibernate_JDBC.pdf

Você tem razão sobre o código dele não mostrar o “loop”, o ideal seria o @Gabriel2017 mostrar o código completo ou esclarecer melhor a dúvida sobre como essas 1000 inserções são feitas, assim terá uma melhor resposta, de repente o problema está no mau uso da ferramenta.

Mas se for para insistir em trabalhar com grande volume de dados processados ao mesmo tempo com Hibernate, que pelo menos use StatelessSession. Só que para quem usa JPA tem que ver como acessar essa implementação do Hibernate.

Sobre StatelessSession: http://codereverse.blogspot.com.br/2012/02/hibernate-melhorando-performance-com.html

A

Antes de continuar, eu gostaria de deixar claro que nem gosto de hibernate, acho que tem outras soluçoes que mantém o equilíbrio entre produtividade e performance bem aceitáveis.
Só acho que essa idéia de que é super lento é um mito espalhado por aí e muito mais exagerado do que a realidade.

Se você pesquisar qualquer coisa na internet querendo confirmar uma opiniao, nao importa o que seja, você vai encontrar.

Agora, analise os links que mandou. No primeiro a pessoa fez um benchmarking com apenas um tamanho de banco de dados, com apenas uma execuçao do programa e tomou aquilo como verdade. E com apenas esse teste ele concluiu que o Hibernate funciona para 1000 linhas mas nao para milhoes.
Será que dá pra concluir isso mesmo com aquele experimento?
E se na verdade o hibernate adicionar um custo constante pra cada query? Teria como ele capturar isso lá?

No segundo artigo a conclusao é que o hibernate é melhor que jdbc. E nem se fala na carga que o hibernate consegue lidar ou nao. Sem falar que afirmaçoes como "Support for Hibernate on Internet is not sufficient. " me fazem acreditar que esse artigo nao é tao recente.

Enfim, o hibernate nunca é minha primeira opçao, mas funciona, inclusive para grandes volumes de dados. E isso falo por experiência, já vi acontecer ( o que obviamente nao te prova nada, mas me dá confiança suficiente pra defender)

G

Galera Muito obrigado pelas respostas, abaixo segue o código que chama o DAO, onde se lê DADO é uma estrutura que contém arrays de informações extraídas, de um determinado documento.

public static void transac(int j){

for (int h = 0; h < Dados.codigo.size(); h++) {			

tmp_insumo_sinapi tmpDados = new tmp_insumo_sinapi();

String codigo = ClienteJpaDAO.getInstance()

.validarDadosTmp(Dados.codigo.get(h), Dados.getUf(), Dados.getMesColeta(), Dados.getAno() ,j);
if(codigo == null){
  		String backup = null; 	
  		
  		if(Dados.precocomDesoneracao.get(h).length()<=6){
  			backup = Dados.precocomDesoneracao.get(h).replace(",", ".");
  		}
  		if(Dados.precocomDesoneracao.get(h).length()>=7 && Dados.precocomDesoneracao.get(h).length()<=15){		
  			backup = Dados.precocomDesoneracao.get(h).replace(".", "");
  			backup = backup.replace(",", ".");
  		}
  						
  		tmpDados.setCodigo(Dados.codigo.get(h));
  		tmpDados.setDescricao_insumo(Dados.descricaoInsumo.get(h));
  		tmpDados.setUnidade(Dados.unid.get(h));
  		tmpDados.setOrigem_preco(Dados.origemPreco.get(h));
  		tmpDados.setPreco_mediano(backup);
  		tmpDados.setUnidade_federativa(Dados.uf);
  		tmpDados.setMes(Dados.mesColeta);
  		tmpDados.setAno(Dados.ano);
  		tmpDados.setDesonerado(j);
  			
  		//INSERIR DADOS NA TABELA TEMPORARIA
  		tmpDados = (tmp_insumo_sinapi) ClienteJpaDAO.getInstance().persist(tmpDados);				
  		
  		//COLETA DADOS DA TB CATALOGO PARA OBTER A CHAVE DA UNIDADE FEDERATIVA INFORMADA					
  		int id_catalogoUF = ClienteJpaDAO.getInstance().consultarItemCatalogo(tmpDados.getUnidade_federativa());				
  						
  		//COLETA DADOS DA TB CATALOGO PARA OBTER A CHAVE DA UNIDADE 					
  		int id_catalogoUND =  ClienteJpaDAO.getInstance().consultarItemCatalogo(tmpDados.getUnidade());	
  		
  		if(id_catalogoUND == 0){
  			tb_item_catalogo registrarUnd = new tb_item_catalogo();
  			registrarUnd.setAtivo(0);
  			registrarUnd.setData_criacao("2016-07-01 00:00:00");
  			registrarUnd.setNome_exibicao(tmpDados.getUnidade().toUpperCase());
  			registrarUnd.setNome(tmpDados.getUnidade());
  			registrarUnd.setOrdem(1);
  			registrarUnd.setId_catalogo(1);
  			registrarUnd = (tb_item_catalogo) ClienteJpaDAO.getInstance().persist(registrarUnd);
  			id_catalogoUND = registrarUnd.getId();
  		}	
  		
  		//CONSULTAR SE JÁ EXISTE O ITEM CADASTRADO NO BD
  		tb_insumo_sinapi_preco teste = new tb_insumo_sinapi_preco();
  		teste =  ClienteJpaDAO.getInstance().validar(tmpDados.getCodigo(), tmpDados.getMes() ,tmpDados.getAno(), id_catalogoUF);
  		
  		if(teste.getId() == 0){
  				
  			//INICIO INSERIR TABELA OFICIAL TB_INSUMO_SINAPI
  			tb_insumo_sinapi tbSinapi = new tb_insumo_sinapi();
  			tbSinapi.setAtivo(1);
  			tbSinapi.setCodigo(tmpDados.getCodigo());
  			tbSinapi.setDescricao(tmpDados.getDescricao_insumo());
  			tbSinapi.setId_unidade(id_catalogoUND);					
  			
  			tbSinapi = (tb_insumo_sinapi) ClienteJpaDAO.getInstance().persist(tbSinapi);				 
  	
  				
  			//INSERIR TABELA OFICIAL TB_INSUMO_SINAPI_PRECO
  			tb_insumo_sinapi_preco tbSinapiPreco = new tb_insumo_sinapi_preco();
  				
  			tbSinapiPreco.setAno(tmpDados.getAno());
  			tbSinapiPreco.setAtivo(1);
  			tbSinapiPreco.setMes(tmpDados.getMes());
  			if(j == 0)
  				tbSinapiPreco.setPreco_desonerado(tmpDados.getPreco_mediano());						
  			else
  				tbSinapiPreco.setPreco_nao_desonerado(tmpDados.getPreco_mediano());	
  			tbSinapiPreco.setId_insumo(tbSinapi.getId());
  			tbSinapiPreco.setId_uf(id_catalogoUF);
  			ClienteJpaDAO.getInstance().persist(tbSinapiPreco);
  				
  			tmpDados.setMigrado(1);
  			tmpDados = ClienteJpaDAO.getInstance().merge(tmpDados);				
  		} else{
  			if(j == 0){
  				if(teste.getPreco_desonerado() == null){
  					teste.setPreco_desonerado(tmpDados.getPreco_mediano());
  					ClienteJpaDAO.getInstance().merge3(teste);
  					
  					tmpDados.setMigrado(1);
  					tmpDados = ClienteJpaDAO.getInstance().merge(tmpDados);
  				}else{
  					System.out.println("ATENÇÃO DADO JÁ CADASTRADO.");
  				}
  			}else{
  				if(teste.getPreco_nao_desonerado() == null){
  					teste.setPreco_nao_desonerado(tmpDados.getPreco_mediano());
  					ClienteJpaDAO.getInstance().merge3(teste);
  					
  					tmpDados.setMigrado(1);
  					tmpDados = ClienteJpaDAO.getInstance().merge(tmpDados);
  				}else{
  					System.out.println("ATENÇÃO DADO JÁ CADASTRADO.");
  				}
  			}					
  		}			
  	} else {
  		System.out.println("Já existe: " + codigo);
  	}
  
  }

}

@AbelBueno
@javaflex

javaflex

Ninguem é obrigado a concordar, sua opnião é importante também. Só te passei um exemplo de como é fácil achar fontes de benchmark e como você mesmo pode testar para saber que JDBC puro é significamente mais eficiente do que Hibernate para processar grande massa de dados.

Hibernate sem fazer nada já entra com um peso chamado sessionfactory, que fica a vida toda na memória de cada aplicação mantida pela empresa.

Sobre internet, recursos defendidos por muitos como vantagem para se valer o custo de adotar hibernate, acabam se anulando. Por exemplo, o acesso a propriedades lazy no back-end não faz o menor sentido para um front-end servido via HTTP, exceto para piorar a performance gerando n+1 querys a toa, o que claramente não é eficiente. Pra fazer um trabalho com eficiencia o programador deve usar sempre HQL/Criteria pra aplicar fetch join e poder gerar um único SQL. Se você trabalha assim ótimo, mas não vejo isso ser mais prático do que escrever SQL diretamente. Outra coisa é o cache, para esse tipo de front-end não tem grande utilidade usar cache no lado servidor. É muita bagagem nas costas da aplicação e do programador, para pouco proveito.

Já para aplicações desktop client-server, ou seja, conectando direto ao banco não tenho nada contra usá-lo, e o cache dele também é bem vindo neste caso, pois o hibernate está no client.

javaflex

Esse array Dado é o que contem 1000 elementos?

Criado 16 de novembro de 2016
Ultima resposta 16 de nov. de 2016
Respostas 9
Participantes 3