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?
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”.
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]
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.