Erro ao Cadastrar Lista no Banco EJB

10 respostas
Victaum

Boa!

Tenho uma lista com aprox. 370 mil itens que tenho q cadastrar em 2 tabelas no banco, de acordo com um agendamento feito em EJB, o problema é q da estouro de memória por volta de 150 itens, sem falar q demora uma eternidade. e ele não cadastra nenhum desses 150 itens. Eu fiz como se fosse uma paginação pra fazer de 10(como exemplo, pq tenho que colocar uma quantidade maior depois), se eu fizer somente uma vez, ou 2 ele cadastrar normalmente mas se eu deixar rolar ele não cadastra. Gostaria de saber se alguem tem uma solução melhor para meu problema, que melhore a performace, ou que pelo menos cadastre tudo no banco!

Bem Editei o topico pra ficar mais leve e ficar mais fácil de ajudar e coloquei em txt os códigos, peço desculpa a todos, e obrigado asaudate pela dica…rsrsrs!

Espero ajuda de vcs, Abrx

10 Respostas

Alexandre_Saudate

Putz… quase travou meu browser…

Quando você tiver MUITO codigo, você pode colocar como anexo, OK ?

[]´s

Victaum

Alguem sabe me dizer se o FastLane Reader resolver o meu problema???

eu realmente preciso de ajuda! :smiley:

abrx

Victaum

Eu to tentando Utilizar o Fastlane, porém não consigo entender, além de não conseguir achar algum exemplo na net!..

alguem conhece algum!???

pdioniziofilho

Vamos la:

  • Vi no log do erro que seu hibernate não está conseguindo abrir conexao:
    “org.hibernate.exception.GenericJDBCException: Cannot open connection”
    Ja arrumou isso?

  • Voce está usando Hibernate para salvar os dados no banco, confere? e voce está lendo isso de um txt?

Tente quebrar isso em blocos, mais ou menos assim: (adapte para suas necessidades)

//no meu caso o arquivo vem aqui, no seu caso voce adapta seu arquivo para o InputStream 
  InputStream entrada = servlet.getResponseBodyAsStream(); 
  byte[] buffer = new byte[32768];//esquema de leitura em blocos
  int i;
  //le de tanto em tanto
  while ((i = entrada.read(buffer)) >= 0) { 
      //faça o que tem que fazer com os dados e chame flush e clear no session 
  }

Não se esqueça no final de cada bloco de escrita de dar um flush no hibernate, para propagar as alterações de memória para o banco e depois dar um clear para limpar os objetos administrados em sessão.

É mais ou menos isso que tu precisa?

{editado}

acho que tenho um exemplo melhor, vou postar já!

Não… muito complicado…

Victaum

Eu salvo os dados no banco mesmo, mas eu não pego esses dados de um txt e sim do próprio banco! lá eu limito a quantidade q ele me retorna tipo de 100 em 100 o problema é q depois da 3 vez ele da esse erro, mas se eu colocar pra executar uma vez só ele salva sem problema!

entendeu!? ou fico confuso!?

rsrsrs…

Tchello

Cara, então, tem como postar o trecho de código que você abre a conexão e persiste o registro?
Posta o método ai e os métodos que você considera importantes também.

Abraços.

Victaum

Esse é o método que seta os valores e salva no banco, está no EJB

public Boolean inclusaoLoteDividaAtivaAmigavel(Timer timer) throws ResourceException, GenericJDBCException, NestedSQLException{

		int ano = Calendar.getInstance().get(Calendar.YEAR);
		int anoMenosSeis = ano - 6;		
		Collection<Pessoa> listaPessoas = new HashSet<Pessoa>();		
		int i = 0;		
		int max = 10;		

		try {

			listaPessoas = pessoaFacade.buscaPessoaseDividasPInclusaoAutomaticaDivAnuidade(ano, anoMenosSeis, max);
			System.out.println("###Tamanho da Lista de Dividas### " + listaPessoas.size());

			if(listaPessoas != null & !listaPessoas.isEmpty()){					

				for (Pessoa pessoa : listaPessoas) {

					for(FinDivida verificaDivida : pessoa.getListaDividas()){

						FinTermosInscricao termoInscricao = new FinTermosInscricao();

						// Verifica se a divida já possui termo
						FinDividaTermosInscricao verificaSePossuiTermo = finDividaTermosInscricaoFacade.verificaSeDividaPossuiTermo(verificaDivida.getCodigo());

						//Se não tiver cadastrada, irá ser cadastrada
						if(verificaSePossuiTermo == null || verificaSePossuiTermo.equals(null)){

							//Seta os Valores que serão salvos no banco
							termoInscricao.setPessoa(pessoa);	
							termoInscricao.setTipoPessoa(pessoa.getTipoPessoa());
							termoInscricao.setFinNatureza(finNaturezaFacade.getById(6L));
							termoInscricao.setDataInclusao(Calendar.getInstance());
							termoInscricao.setFinFase(finFaseFacade.buscarFasePorCodigo(1l));
							termoInscricao.setFinProcesso(null);
							termoInscricao.setObservacao("INCLUSÃO DE DÍVIDA DE ANUIDADE");
							termoInscricao.setAtivo(true);
							termoInscricao.setNumero(geradorSequenciaOficioDAO.getSequenciaNumeroTermoInscricao("RDA"));

							//Cadastra no banco
							finTermosInscricaoFacade.cadastrar(termoInscricao);

							for(FinDivida divida : pessoa.getListaDividas()){

								//Cria uma nova instancia e seta os dados na tabela									
								FinDividaTermosInscricao dividaTermoInscricao = new FinDividaTermosInscricao();

								//Busca o termo cadastrado pra cadastrar na tabela Fin_Divida_Termos_Inscricao 
								FinTermosInscricao termo = finTermosInscricaoFacade.buscaTermoInscricao(termoInscricao.getCodigo());

								dividaTermoInscricao.setFinDivida(divida.getCodigo());					
								dividaTermoInscricao.setFinTermoInscricao(termo.getCodigo());									

								//Cadastra os dados na tabela
								finDividaTermosInscricaoFacade.cadastrarDivida(dividaTermoInscricao);									
							}

						}else{
							System.out.println("Já existe termo para esta dívida! " + verificaDivida.getCodigo());
							System.out.println("Dívida! " + verificaDivida.getNatureza().getDescricao());
						}							
					}
					i++;						
				}
				System.out.println("=====No for em inclusaoLoteDividaAtivaAmigavel===== " + i);
				System.out.println("MAX>>> " + max);

				System.out.println("Foram incluidos "+i+" em divida ativa");

				long fim = System.currentTimeMillis();
				double tempo = (fim - inicio) / 1000.0;
				System.out.println("Tempo gasto: " + tempo);
				System.out.println("Memória " + Runtime.getRuntime().freeMemory());
				Runtime.getRuntime().gc();
				System.out.println("Limpo a memória! " + Runtime.getRuntime().freeMemory());
				return true;
			}else{
				return false;
			}				

		} catch (RuntimeException e) {
			e.printStackTrace();
			return false;
		}		
	}

Esse método cadastra uma das dívidas

public void cadastrarDivida(FinDividaTermosInscricao dividaTermoInscricao){		

		if(dividaTermoInscricao.getDividaOriginal() == null){
			final String INSERT_SQL = "INSERT INTO FIN_DIVIDA_TERMOS_INSCRICAO (FK_CODIGO_DIVIDA, FK_CODIGO_TERMOS_INSCRICAO)" +
			" values (? , ?)";		
			Query query = em.createNativeQuery(INSERT_SQL);
			query.setParameter(1, dividaTermoInscricao.getFinDivida().toString());
			query.setParameter(2, dividaTermoInscricao.getFinTermoInscricao().toString());
			query.executeUpdate();
		}else{
			
			final String INSERT_SQL = "INSERT INTO FIN_DIVIDA_TERMOS_INSCRICAO (FK_CODIGO_DIVIDA, FK_CODIGO_TERMOS_INSCRICAO, FK_CODIGO_DIVIDA_ORIGINAL)" +
			" values (?, ?, ?)";		
			Query query = em.createNativeQuery(INSERT_SQL);
			query.setParameter(1, dividaTermoInscricao.getFinDivida().toString());
			query.setParameter(2, dividaTermoInscricao.getFinTermoInscricao().toString());
			query.setParameter(3, dividaTermoInscricao.getDividaOriginal().getCodigo().toString());
			query.executeUpdate();			
		}

	}

Esse outro cadastra tb as dividas

public FinTermosInscricao cadastrar(FinTermosInscricao termoInscricao){			
               em.persist(termoInscricao);		
		return termoInscricao;
	}

Lembrando que se eu fizer uma vez só e mandar cadastrar os 100 primeiros ele cadastra, mas seu eu fizer tipo paginação e mandar de 10 em 10 ele da o erro citado acima e não cadastra nenhum!

sergiotaborda

O seu problema não é apenas ter muitos itens, é ter uma transação muito longa.
Porque vc está em ambiente EJB vc perde controle disso pois o commit só acontecerá quando o AS quiser.

EJB não serve para fazer processos batch.

Primeiro vc precisa ter controle da transação e comitar verdadeiramente a cada N registros.

Fastlane vai ajudar no seu problema mas para isso vc não pode usar JPA/Hibernate. Use JDBC puro e use o ResultSet diretamente.
O ResultSet já é um fastlane, então não terá problemas de memoria…

Pagine a pesquisa , trazendo apenas itens de y em y ( digamos que N = 10 , y = 100)
Use PreparedStatement em modo batch assim vc consegue acelerar o processo do lado do servidor (se o seu driver suportar isso)

O codigo seria algo assim:

PreparedStatement  preparedStatementDeLeitura = con.preapreStatement( "um select paginado dos dados");
PreparedStatement preparedStatementDeEscrita = con.prepareStatement(" INSET INTO tabela (campo1, campo2) values (?,?)");
ResultSet rs = preparedStatementDeLeitura.executeQuery();

count==0;

while (rs.next()){
 preparedStatementDeEscrita.setString(rs.geString('campo1'));
 preparedStatementDeEscrita.setInteger(rs.getInteger('campo2'));
 preparedStatementDeEscrita.addBatch();
 count++;
if (count % 10 ==0 ){
      preparedStatementDeEscrita.execute();
      con.commit(); // commita de 10 em 10
}

}
con.commit();

entenda o ResultSet como um stream de input e o preparedStatemet como um stream de outrput. Vc tem que manter o fluxo dando flush (coomit) frquentemente. Senão o negocio trava por timeout

Batch não é trivial. se pouder use o Spring Bacth

Victaum

Cara muito obrigado vou tentar aqui! se eu conseguir eu posto aqui novamente… e se não conseguir tb…rsrsrsrs

abrx e obrigado a todos!

Tchello

Fiz uma alteração num sistema que inseria muitos dados numa única rotina mas do modo bruto com JDBC e coloquei PreparedStatement e batch.
Ficou muito melhor!
E fiz o commit só no final, isso por que inseria bastante registro, coisa de 15 a 30 mil por vez.

Criado 19 de fevereiro de 2010
Ultima resposta 23 de fev. de 2010
Respostas 10
Participantes 5