Hibernate - instabilidade recuperando lista objetos atualizados

11 respostas
F

Estou enfrentando problemas com hibernate que parece estar relacionado ao objeto de cache.

Eu tenho uma entidade, vamos chamá-la A, com um atributo lista mapeada com @OneToMany (cascade = CascadeType.ALL, mappedBy = “atributo”). Os objetos da lista são entidades de classe B e tem um relacionamento bidirecional com A. Então B tem um atributo mapeado com @ManyToOne (cascade = CascadeType.MERGE). Recebo o objeto A partir do banco de dados e adiciono um novo objeto B na lista A usando a chamada e Session.update (A).

O problema surge quando eu listo todos os objetos do tipo A, usando a chamada “session.createCriteria (A.class) .list ();”, acesso exatamente a um objeto A que eu atualizei e o objeto B que eu adicionei NÃO está presente na lista. Ao abrir o bando de dados (postgres) tanto o objeto A quanto o objeto B estão atualizados e corretamente salvos no banco, porém na consulta realizada (Criteria) os dados não são os mesmos algumas vezes, hora a quantidade de objetos B está correta, hora está faltando algum elemento B na lista.

Tentei usar session.refresh (A) após a atualização A, mas o problema ainda persiste.

11 Respostas

rodrigo.uchoa

Hmmmm… Não sei se isso vai te ajudar, mas você já tentou setar os dois lados da relação? Digo, você adicionou um objeto B a uma coleção em um objeto A, e setou esse mesmo objeto A na referencia ManyToOne do objeto B? Tipo:

a.getList().add(b); b.setA(a);

Principalmente se a sua pesquisa ocorre na mesma sessão onde foi feito o update, não vejo nenhum motivo para o objeto B não estar lá. Exceto se for algum problema de equals()/hashCode().

F

Ola Rodrigo, eu já estou setando o objeto nos dois lados da relação. O problema é uma instabilidade no retorno da consulta no banco, pois em algumas consultas o objeto B que adicionei na lista aparece, e se eu repetir a mesma consulta (ou refresh na mesma tela) o objeto B não está mais na lista.

rodrigo.uchoa

A consulta que apresenta problemas é feita na mesma sessão do update que adicionar o objeto à lista? Você já implementou o equals()/hashcode(), só por desencargo de consciência?

Se for possível coloca aqui os trechos de código onde isso ta dando errado.

alexfe

Faça dois testes iniciais antes de sua criteria
Primeiro chame o evict da session passando o seu objeto. Esse metodo retira da sessao e da memoria
Segundo teste chame o clear da session. Esse metodo limpa a sessao
Faca esses dois testes para sabermos se o problema está ligado a memoria.

F

Rodrigo,
A consulta que apresenta problemas é feita na mesma session, pois nossa classe GenericDao foi implementada com ThreadLocal como atributo da classe, conforme apresentado abaixo:

private static final ThreadLocal threadSession = new ThreadLocal();
        ......

	public Session getSessionHibernate() {
		Session s = (Session) GenericDao.threadSession.get();
		// Open a new Session, if this thread has none yet

		if (s == null) {
			s = this.getHibernateTemplate().getSessionFactory().openSession();
			GenericDao.threadSession.set(s);
		}
		return s;
	}

	public void update(final T object) throws HibernateException {
		try {
			final Session s = this.getSessionHibernate();
			s.update(object);			

		} catch (final HibernateException ex) {
			GenericDao.logger.error(ex);
			
			Session s = this.getSessionHibernate();
			s.getTransaction().rollback();
			s.clear();
			
			throw new HibernateException(ex);
		}
	}

	public List<T> listAll() throws HibernateException {
		try {
			final Session s = this.getSessionHibernate();
						
			final Criteria c = s.createCriteria(this.objectClass);
			return c.list();
			
		} catch (final HibernateException ex) {
			GenericDao.logger.error(ex);
			
			Session s = this.getSessionHibernate();
			s.getTransaction().rollback();
			s.clear();
			
			throw new HibernateException(ex);
		}
	}

O método equals está implementado e o hascode não, será que o problema pode ser esse?

alexfe

Sempre implemente equals e hashcode. Eu sempre implemento dando uma atenção para as Primaries keys

F

Olá alexafe,

Já implementei os métodos equals e hashcode e o problema permanece. Um comportamento que observo é que os objetos da lista que foram adicionados em starts anteriores do servidor nunca "desaparecem" da consulta, apenas os novos, inseridos na sessão atual que ora aparecem e depois não aparecem mais em uma nova consulta.

Sobre os testes que você pediu:

Teste 1: chamando o evict da session passando o objeto:
public List<T> listAll() throws HibernateException {
		try {
			final Session s = this.getSessionHibernate();
			s.evict(this.objectClass);
			final Criteria c = s.createCriteria(this.objectClass);
			return c.list();
			
		} catch (final HibernateException ex) {
			GenericDao.logger.error(ex);
			
			Session s = this.getSessionHibernate();
			s.getTransaction().rollback();
			s.clear();
			
			throw new HibernateException(ex);
		}
	}
Teste 2: chamando o clear da session
public List<T> listAll() throws HibernateException {
		try {
			final Session s = this.getSessionHibernate();
			s.clear();
			final Criteria c = s.createCriteria(this.objectClass);
			return c.list();
			
		} catch (final HibernateException ex) {
			GenericDao.logger.error(ex);
			
			Session s = this.getSessionHibernate();
			s.getTransaction().rollback();
			s.clear();
			
			throw new HibernateException(ex);
		}
	}
Em ambos os testes o problema permanece o mesmo. No teste 2, um erro é apresentando informando que a sessão está "closed".

Desde já, obrigado pela atenção de todos.

alexfe

Cham o flush da Session do hibernate após vc dar o Update.

F

Alexafe,

ainda não consegui resolver o problema, mesmo chamando o flush após o update.

public void update(final T object) throws HibernateException {
		try {
			final Session s = this.getSessionHibernate();
			s.beginTransaction();
			s.update(object);
			s.flush();
			s.getTransaction().commit();

		} catch (final HibernateException ex) {
			GenericDao.logger.error(ex);
			
			Session s = this.getSessionHibernate();
			s.getTransaction().rollback();
			s.clear();
			
			throw new HibernateException(ex);
		}
	}

Segue abaixo o mapeamento resumido feito nos objetos:

@Entity
public class EvaluationProject implements Serializable{
 
	/**
	 * ID generated by the database
	 */
	@Id
	@GeneratedValue
	private Long id;

	/**
	 * The list of rounds of the project evaluation
	 */
	@OneToMany(cascade=CascadeType.ALL, mappedBy="project")
	private Set<EvaluationRound> evaluationRoundList;
}

@Entity
public class EvaluationRound implements Serializable{
 
	/**
	 * ID generated by the database
	 */
	@Id
	@GeneratedValue
	private Long id;
	 
	/**
	 * The round's name
	 */
	@Column(nullable=false)
	private String roundName;	 
	
	/**
	 * The project
	 */
	@ManyToOne(cascade=CascadeType.MERGE)
	private EvaluationProject project;

- O problema não acontece quando estou adicionando novos rounds no projeto (update), pois os novos rounds são salvos no banco. O problema acontece na consulta (listAll), quando tento listar todos os projetos e seus respectivos rounds. Na mesma página, dando refresh (acessando o mesmo link), a quantidade de rounds em um determinado projeto fica instável, ora aparece 3 que eu adicionei e após um refresh na tela aparece apenas 1, isso é aleatório. A quantidade de projetos exibidos não altera, o que altera é apenas a quantidade de rounds exibidos, e apenas os rounds que eu adicionei fazendo update.

Segue código da consulta:

public List<T> listAll() throws HibernateException {
		try {
			final Session s = this.getSessionHibernate();
			final Criteria c = s.createCriteria(this.objectClass);
			return c.list();
			
		} catch (final HibernateException ex) {
			GenericDao.logger.error(ex);
			
			Session s = this.getSessionHibernate();
			s.getTransaction().rollback();
			s.clear();
			
			throw new HibernateException(ex);
		}
	}
alexfe

Ta vamos tentar mais algumas alternativas

Altere

para

e tb altere

final Session s = this.getSessionHibernate();  
            final Criteria c = s.createCriteria(this.objectClass);  
            return c.list();

por

Session s = this.getSessionHibernate(); Query c = s.createQuery("from " + this.objectClass); return c.list();

rodrigo.uchoa

Lembra do seguinte… Se as suas coleções são do tipo Set<?>, como parecem que são de acordo com seu código, a sua implementação de equals() e hashCode() NÃO PODEM usar a chave primária se ela for gerada automaticamente (auto-increment, sequence, etc).

Isso porque as chaves só serão geradas depois do flush, e vai quebrar a semântica do Set. Tenta criar o equals() e hashCode() usando outros atributos obrigatórios. Esse erro inclusive é compatível com o problema que você ta experimentando.

Criado 19 de novembro de 2014
Ultima resposta 25 de nov. de 2014
Respostas 11
Participantes 3