Hibernate - instabilidade recuperando lista objetos atualizados

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.

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().

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.

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.

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.

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?

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

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:
[code]public List 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);
	}
}[/code]

Teste 2: chamando o clear da session
[code]public List 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);
	}
}[/code]

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.

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

Alexafe,

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

[code]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);
	}
}[/code]

Segue abaixo o mapeamento resumido feito nos objetos:

[code]@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;

[/code]

  • 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:

[code]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);
	}
}[/code]

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();

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.