[RESOLVIDO] Hibernate AS VEZES não salva!

Estou com problema semelhante ao tópico abaixo só que a diferença é que eu chamo o flush antes de cada commit no banco.
http://javafree.uol.com.br/topic-868046-Hibernate-diz-que-Salva-mas-no-banconao-salva-rs.html

Estou com um sistema aqui rodando com cerca de 15 usuários simultâneos e em um desses clientes (aleatório, já ocorreu com todos :S) ocorre o problema 1x no dia.Quando o problema ocorre, esse erro acontece em todos os cadastros, no entanto quando ele abre o sistema novamente trabalha bem o resto do dia.
Então, tá complicado para eu simular aqui e realizar os testes como deveriam ser feitos.

Você tem alguma sugestão de como eu posso resolver esse problema? (a dica do cache descrita no tópico só serviria caso eu não fizesse o flush toda vez, correto?)
Se eu usasse o Spring para gerenciar as transações eu estaria livre desse problema?

Atenciosamente,
Rafael Viana

afaelViana cola ai um exemplo de como vc salva os dados ou como é o seu hibernateUtil, talvez assim fique mais fácil para galera ajudar.

Como eu salvo os registros:

[code]this.session = HibernateUtil.openSession(id);
this.tx = this.session.beginTransaction();

this.session.saveOrUpdate(objeto);
this.session.flush();
tx.commit();[/code]

Trechos do HibernateUtil.java

Trabalho com um banco de dados para cada empresa, então armazeno em um HashMap o sessionFactory de cada empresa.Então, toda vez para abrir uma sessão passo um id da empresa para achar o factory no HashMap.

[code]public static Session openSession(String id) {
return getSessionFactory(id).openSession();
}

private static Map<String,SessionFactory> factoryMap = new HashMap<String,SessionFactory>();

public static SessionFactory getSessionFactory( String empresa ) {

SessionFactory sessionFactory = factoryMap.get(empresa);   
      
    if(sessionFactory == null) {  
    	  	
    	AnnotationConfiguration cfg = new AnnotationConfiguration();
    	
    	cfg.configure("/" + empresa + ".cfg.xml");
    	
    	//declaro todas as classes que existem no sistema
	cfg.addAnnotatedClass(Tabela.class);
            
           //crio a session factory
       sessionFactory = cfg.buildSessionFactory();
			
	//coloco ela na lista, para não precisar criá-la todas as vezes
	factoryMap.put( empresa, sessionFactory );   
          
      }
      
      return sessionFactory;
      
   }[/code]

Olá!
Eu faria umas mudanças ai, deixaria o HibernateUtil assim:

public class HibernateUtil {

	private static SessionFactory factory;

	static {
		AnnotationConfiguration cfg = new AnnotationConfiguration();
		cfg.addAnnotatedClass(Tabela.class);
		cfg.addAnnotatedClass(OutraClasse.class);
		cfg.addAnnotatedClass(OutraClasse.class);
		factory = cfg.buildSessionFactory();
	}
	public Session getSession() {
		return factory.openSession();
	}
}

A classe acima possui o método getSession() que será chamado pela DAO abaixo.
Na DAO eu criaria três métodos, um para salvar, outro para deletar e outro para fazer update:

public class TabelaDAO {
	
private Session session;
	
	public TabelaDAO(Session session) {
	this.session = session;
    }

	public TabelaDAO() {		
		this.session = new HibernateUtil().getSession(); // aqui eu pego o getSession();
	}	

public void salva(Objeto objeto) {
		Transaction tx = session.beginTransaction();
		session.save(objeto);
		tx.commit();
	}	
public void atualiza(Objeto objeto) {
		Transaction tx = session.beginTransaction();
		session.update(objeto);
		tx.commit();
	}

	public void remove(Objeto objeto) {
		Transaction tx = session.beginTransaction();
		session.delete(objeto);
		tx.commit();
	}
}

Veja se ajuda.
Abraço!

@Guevara

Obrigado, mas:

1)Com esse HibernateUtil como eu vou trabalhar com mais de uma session factory? Também utilizo esse modelo que você citou em outros programas onde só preciso de um banco de dados.

No meu caso tenho:
Empresa A | SessionFactory A (ligada ao banco de dados A)
Empresa B | SessionFactory B (ligada ao banco de dados B)

Por isso tenho aquela estrutura.

2)Não posso ter parâmetros no construtor, pois utilizo o Adobe Flex na camada de visão e um dos requerimentos do BlazeDS(framework para integrar no Java) é o construtor sem parametros.

Fiquei curioso, Porque você indicaria separar o salvar e o atualizar?

Vc está fazendo um session.close() depois que o método executa o commit?

Oi Rafael!
Desculpe, não sabia como vc estava desenvolvendo a sua aplicação. Eu gosto de separar os métodos, a parte de acesso a dois bancos eu já vi em aplicação Desktop, mas o código era Java puro e usava arquivo bd.properties com o seguinte conteúdo:
Exemplo:

driver=org.postgresql.Driver

bd=jdbc:postgresql://localhost:5432/Empresa
usuario=postgres

senha=123456

Para Web dá pra fazer tb, mas teria que dar uma pesquisada pra ver como ficaria o HibernateUtil. Acho que aqui mesmo no GUJ deve ter código mostrando como é.
Abraço!

Não sei se teria algum efeito, mas tu poderia isolar os teus sessionFactory em uma ThreadLocal…

 private static ThreadLocal&lt;HashMap&lt;String,SessionFactory&gt;&gt; factorys = new ThreadLocal&lt;HashMap&lt;String,SessionFactory&gt;&gt;()


   
 public static SessionFactory getSessionFactory( String empresa ) {     
             
     
         if(factorys.get() == null){
             factorys.put(new HashMap&lt;String,SessionFactory&gt;())
         }     
         SessionFactory sessionFactory = factorys.get().get(empresa);     
         if(sessionFactory == null) {    
                   
             AnnotationConfiguration cfg = new AnnotationConfiguration();  
               
             cfg.configure("/" + empresa + ".cfg.xml");  
               
             //declaro todas as classes que existem no sistema  
         cfg.addAnnotatedClass(Tabela.class);  
                   
                //crio a session factory  
            sessionFactory = cfg.buildSessionFactory();  
                   
         //coloco ela na lista, para não precisar criá-la todas as vezes  
         factorys.get().put( empresa, sessionFactory );     
                 
           }  
             
           return sessionFactory;  
             
        }  

@romarcio Sim, faço o session.close() no finally de cada interação com o banco de dados

@Guevara Opa valeu…

@Fernando Vou dar uma olhada sim


O que mais me intriga é que isso ocorre somente as vezes e sem nenhuma lógica.Se ocorresse sempre e no mesmo método eu poderia verificar se não é uma sessão que deveria ficar aberta e não está ou alguma outra coisa.

Tenho percebido que as respostas são voltadas para o gerenciamento das sessões e transações (grande probalidade de serem os ‘culpados’), não seriam esses os principais benefícios em utilizar o Spring?por isso faço uma pergunta:

Alguém com experiência no framework Spring:
Trabalhar com o Spring integrado no Hibernate é essa maravilha toda como falam? Ou também ocorrem problemas com o gerencimento de transações/sessões?

Ola Rafael

Voce nao precisa chamar o flush se voce nao mexeu no FlushMode da session. Esse nao é o problema

Que exception aparece quando fica sem salvar? Se o commit falha, a exception é pega onde? Onde tem try catch disso?

Segue abaixo o método completo para salvar cliente:


public Cliente save(String id, Cliente cliente) throws Exception {
		
		try {
			
			this.session = HibernateUtil.openSession(id);
			this.tx = this.session.beginTransaction();
			
			this.session.saveOrUpdate(cliente);
			this.session.flush();
			tx.commit();
			
			return cliente;
			
		} catch(GenericJDBCException e) {
			
			if( tx != null && tx.isActive() )
				this.tx.rollback();
			
			throw new DaoException("Não foi possível acessar o servidor. Tente novamente em alguns minutos.", e);
			
		} catch (ConstraintViolationException e) {
			
			System.out.println("--------------------------------Houve erro ao salvar cliente----------------------------------");
			System.out.println("Cliente: " + cliente );
			
			if( cliente != null )
				System.out.println("Cliente Código: " + cliente.getCli_codigo() );
			
			if( tx != null && tx.isActive() )
				this.tx.rollback();
			
			throw new DaoException("Não foi possível salvar, pois já existe um cliente com este nome", e);
		
		} catch (Exception e) {
			
			System.out.println("--------------------------------Houve erro ao salvar cliente 2----------------------------------");
			System.out.println("Cliente: " + cliente );
			
			if( cliente != null )
				System.out.println("Cliente Código: " + cliente.getCli_codigo() );
			
			if( tx != null && tx.isActive() )
				this.tx.rollback();
			
			throw new FlexException("Ocorreu um erro não reconhecido pelo sistema.Entre em contato com o suporte para solucionar esse problema.", e);
			
		} finally {
			
			//a sessão não está nula e está aberta
			//então preciso fechar para não ocorrer vazamentos
			if(session != null && session.isOpen())
				this.session.close();
			
		}
		
	}

@Paulo

Isso que tá complicado de entender… não dá exceção… (por isso acho que seja um erro na sessão ou no commit :S que essas informações estejam se perdendo no caminho até chegar ao banco de dados?).

Para melhorar o entendimento:

Utilizo Flex na camada de visão.Então, quando clico para gravar um cliente o BlazeDS(framework do Flex para ligar no Java) chama essa classe do Java, se tudo ocorreu com sucesso é retornado um ResultEvent (tudo deu certo e eu pego o objeto cliente que está no return) ou um FaultEvent (caso uma exceção seja disparada).

Vários clientes já haviam reclamado desse erro, mas achei que não podia dar erro porque por mais que eu testasse tudo funcionava perfeitamente, até que sexta-Feira passei por uma situação inusitada (vivenciei o erro), pois cliquei para salvar um novo cliente o Java me retornou o objeto com o código (então aparentemente tudo havia ocorrido com sucesso, não havia nenhuma exceção), porém ao salvá-lo novamente tomei uma exceção:

org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

Fui verificar o banco de dados e o cliente realmente não estava lá… Verifiquei aqui nos logs e nenhuma exceção havia sido disparada.

QUALQUER outro cadastro que eu tentasse manipular (inserir/editar/excluir) não sofria as alterações… abri novamente o sistema e o erro persistia. até que reiniciei o Tomcat (recriou a session factory), então tudo funcionou normalmente.

Talvez uma “pane” na SessionFactory? Só que outras vezes não preciso reiniciar o Tomcat para “resolver” o problema então não está todo na SessionFactory.

Mais informações…

Parece que quando o sistema em produção o erro ocorre com uma frequencia maior (está pior do que pensava…), pois estava aqui testando e achei novamente.Aproveitei para realizar mais alguns testes:

Salvei o cliente, porém ele não estava no banco de dados conforme comentei acima.Tentei salvar itens em cadastros mais simples TODOS dão esse erro. Salvo o item não ocorre nenhuma exceção, porém o registro não é salvo.

Aproveitei para verificar o que ocorre quando faço alterações.

  1. Altero o registro
  2. Faço uma pesquisa na lista
  3. O registro está com as alterações (parece que está correto, isso é péssimo pois o usuário acha que está tudo normal…)
  4. Mas ele não foi alterado no banco de dados
  5. Quando o sistema é reaberto nada foi alterado
  6. O cliente fica $¨&$@&*

Acho que o Hibernate altera o que está na sessão, mas essa alteração não está conseguindo chegar até o banco.Pode ser isso? (não teria que ocorrer uma exceção? )


Quando eu abri o sistema novamente, tudo estava funcionando normalmente de novo.Isso elimina algum erro geral na SessionFactory.

Só que se o erro estivesse com alguma sessão especifica esse problema ocorreria apenas na sessão que abri e tentei dar o commit (porque sempre abro sessões novas).Como tentei salvar em vários cadastros e sempre dava o erro, está ocorrendo o mesmo erro para todas as sessões que são abertas :S

oi Rafael

Voce vai precisar mesmo isolar o caso pra gente descobrir o problema.

Sobre o StaleObjectException, acontece quando falou o lock otimista do @Version… isso é normal caso voce esteja usando o lock otimista e duas pessoas editem o objeto “ao mesmo tempo”.

@Paulo

Sobre a StateStaleException:

Achei esse tópico
http://www.guj.com.br/posts/list/132646.java

No meu caso deve estar ocorrendo por isso
1 - Quando você tentar fazer uma operação de escrita em um registro da qual o hibernate não se tem mais a instãncia.
Já que não haviam duas pessoas editando o mesmo objeto ao mesmo tempo.

O que você diz com isolar o caso? Criar um projeto para analisarem o código?

Eu tive um problema assim em uma aplicação, em que eu mandava salva alguma coisa e o hibernate agia como se estivesse feito, porem nada era salvo e nenhuma exeção era gerada.

Eu utilizava a versão hibernate-3.2.4.jar, quando troquei este jar por outra versão, passou a funcionar perfeitamente, desde então não usei mais a versão 3.2.4.

Caso seja esta a versão que esteja usando, troque por uma mais atual, só para tirar a dúvida.

Ola Rafael,

também estou desenvolvendo uma aplicação Flex/Blaze/Hibernate, no meu caso era algo mais de ambiente, usando Eclipse, quando ocorria uma exceção na parte do Java, o stacktrace era ‘ocultado’, então eu chutei o pau da barraca e debuguei na mao, colocando o metodo num try {} catch {Exception e} e analisando a excecao pelo Inspect. Já foram várias tentativas, quem sabe nao seja isso.

Nao sei se influencia tanto, versao do meu Hibernate é a 3.2.5

aqui eu estava usando o hibernate 3.2.alguma coisa, tinha uma parte do código onde eu dava um flush para depois dar um refresh em um objeto especifico (antes do commit na transaction do hibernate) … as 17 horas eu fui testar essa ultima funcionalidade antes de apresentar no dia sequinte as 9 horas… foi um momento ruim para dar esse erro abaixo:

010-07-22 17:13:35,492 ERROR org.hibernate.AssertionFailure.&lt;init&gt;(AssertionFailure.java:22) an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session) org.hibernate.AssertionFailure: null identifier at org.hibernate.engine.EntityKey.&lt;init&gt;(EntityKey.java:39) at org.hibernate.event.def.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:74) at org.hibernate.event.def.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:39) at org.hibernate.impl.SessionImpl.fireRefresh(SessionImpl.java:902) at org.hibernate.impl.SessionImpl.refresh(SessionImpl.java:886) at net.upaid.cvc.struts.actions.AceitarProposta.execute(AceitarProposta.java:149) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:441) at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:280) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:243) at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:165) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:252) at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:122) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:195) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:195) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:179) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:75) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:94) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:235) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:89) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:130) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:267) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:126) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:138) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:165) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:179) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:176) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237) at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52) at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:488) at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77) at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)

tinha uma mensagem também que não devo ter copiado onde dizia que isso provavelmente era algum erro no framework…

Aqui estou com o Hibernate 3.5

Mas estou pensando seriamente em começar a usar o Spring para ver se esse problema resolve… (porque não consigo pensar nenhuma solução para esse problema :shock: ).

Consegui simular a situação aqui, esse erro acontece na seguinte situação:

Estou com a aplicação cliente aberta, reinicializo o Tomcat, com a mesma aplicação que estava aberta eu crio um novo cliente ele não é salvo, quando tento salva-lo novamente acontece o erro do StaleStateException (porque o objeto não estava na sessão - o que confirma o erro de não salvar).


Habilitei as estatisticas do SessionFactory:

Quando salvo um cliente em uma situação normal: 1 transação aberta, 1 com sucesso
Quando salvo um cliente após o restart do Tomcat: 2 transações abertas, 1 com sucesso (no finally do bloco ao pegar a variavel wasCommited me informava que a transação foi efetuada com sucesso…)


Rodei essas mesmas simulações com o Hibernate Profiler para verificar quais das transações falhava:

->Situação normal (tudo ocorre normalmente):

begin transaction with isolation level: unknown
INSERT INTO cliente
INSERT INTO revinfo
INSERT INTO cliente_AUD
commit transaction

->Após o restart acontece um erro ao salvar as informações do Envers então é feito o rollack na transação (cancelando a alteração feita de salvar o cliente).
begin transaction with isolation level: unknown
INSERT INTO cliente
rollback transaction
ERROR: exception calling user Synchronization
commit transaction porque deu o commit aqui ?!?!

Então parece que o Envers é que está escondendo a exception:

Achei uma situação aqui:
http://community.jboss.org/thread/149037?start=0&tstart=0

Em um dos posts: “When hibernate executes the transaction synchronization mechanism, any exception throwed by any implementation of the Synchronization interface (like AuditSync) is discarded by hibernate. So the exception does not propagates.”


Ao desabilitar o Envers simulei ambas as situações e TUDO COM SUCESSO!

Ok… o problema DEVERIA estar no Envers.
Criei um projeto novo para simular essa situação, e então iria entrar em contato com os desenvolvedores.Porém, ao simular nesse projeto que criei TUDO COM SUCESSO… (mesmo com Envers ativado…)


Enquanto estava escrevendo esse post achei a solução :smiley: Mas fica todo o relato…

Então, me lembrei que no Envers uso uma Entidade extendida para também gravar quem é o usuário que está logado.Coloquei-a no projeto de exemplo e não funcionou.

O erro acontece, pois ao salvar qualquer entidade pego o nome do usuário que está armazendo.Mas como o tomcat foi reinicializado esse objeto não está mais lá e acontece o erro. NullPointerException (que ficou escondida…) como essa exceção é pega é feito o rollback na transação cancelando a alteração/inclusão.

Antes deixava meu objeto no FlexClient (que morria no restart)
FlexContext.getFlexClient().setAttribute(“usuario”, usuario_logado.getUsu_nome());

Agora deixo no FlexSession(Sessão que é para ser a mesma que o HttpSession)
FlexContext.getFlexSession().setAttribute(“usuario”, usuario_logado.getUsu_nome());

Problema Resolvido!