[RESOLVIDO] Sincronizar objetos sem desconectar do Banco de Dados

Ola Pessoal

Estou trabalhando com JPA 2.0 (Hibernate) em um sisteminha Desktop.

Estou trabalhando com uma única conexão, ou seja, crio o DAO gernérico com o EntityManager ao abrir o sistema e utilizo ele para todas as operações necessárias.

Acontece que quando eu deleto uma Objeto (entidade), ele some do banco de dados, mas se eu fizer uma consulta por NamedQuery ele continua aparecendo, ele só some se eu desconectar do banco de dados e conectar denovo, ou seja, se eu o programa e abrir denovo.

Já estou utilizando o método “flush()” no momento de inserir, alterar e excluir objetos, mas isso não resolveu, veja abaixo minha classe DAO.

package dao;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class DAO {

    private EntityManager em;

    /**
     * Método construtor que recebe um Objeto EntityManager.
     * @param em Objeto do tipo EntityManager.
     */
    public DAO(EntityManager em) {
        this.em = em;
    }

    public DAO() {
    }

    /**
     * Este método insere um objeto utilizando o EntityManager.
     * @param objeto Objeto da entidade do seu sistema.
     */
    public void insere(Object objeto) {
        if (!daoValido()) {
            return;
        }
        em.getTransaction().begin();
        em.persist(objeto);
        em.flush();
        em.getTransaction().commit();
        try {
            em.refresh(objeto);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Este método encontre um Objeto no banco a partir do número de ID.
     * @param classe Tipo de Objeto que será retornado.
     * @param id Número no formato Inteiro que representa o ID do objeto.
     * @return Objeto encontrado a partir do parámetro passado no ID.
     */
    public Object buscaID(Class classe, String id) {
        if (!daoValido()) {
            return new Object();
        }
        Long numeroId;
        if (id.isEmpty() || id == null) {
            numeroId = Long.parseLong("0");
        } else {
            numeroId = Long.parseLong(id);
        }
        Object objeto = em.find(classe, numeroId);
        try {
            em.refresh(objeto);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return objeto;
    }

    /**
     * Este método atualiza atualiza um Objeto no Banco de Dados de acordo com seus parâmetros.
     * @param objeto Objeto a ser atualizado no banco de dados.
     */
    public void atualiza(Object objeto) {
        if (!daoValido()) {
            return;
        }
        em.getTransaction().begin();
        em.merge(objeto);
        em.flush();
        em.getTransaction().commit();
        try {
            em.refresh(objeto);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Este método remove um Objeto cadastrado no banco de dados.
     * @param objeto Objeto que será removido do banco de dados.
     */
    public void remove(Object objeto) throws Exception {
        if (!daoValido()) {
            return;
        }
        try {
            em.refresh(objeto);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        em.getTransaction().begin();
        em.remove(objeto);
        em.flush();
        em.getTransaction().commit();
    }

    /**
     * Este método retorna uma lista de objetos de acordo com o SELECT em SQL passado por parâmetro.
     * @param selectSql Comando SQL contendo o SELECT desejado.
     * @return Lista genérica de objetos encontrados no campo de dados.
     */
    public List seleciona(String selectSql) {
        if (!daoValido()) {
            return new ArrayList();
        }
        List lstResultado = em.createQuery(selectSql).getResultList();
        return lstResultado;
    }

    /**
     * Este método retorna uma lista contendo todos os objetos do Tipo que lhe for indicado.
     * @param classe String contendo o Nome da classe no qual os objetos serão retornados.
     * @param ordernarPor String contendo o Nome do atributo da classe no qual a lista será ordenada.
     * @return Lista Lista genérica de objetos encontrados no campo de dados.
     */
    public List selecionaTodos(Class classe, String ordernarPor) {
        if (!daoValido()) {
            return new ArrayList();
        }
        List lstResultado = em.createQuery("SELECT x FROM " + classe.getSimpleName() + " x ORDER BY x." + ordernarPor).getResultList();
        return lstResultado;
    }

    private boolean daoValido() {
        if (em == null) {
            System.err.println("DAO INVALIDO: erro ao tentar utilizar DAO");
            return false;
        }
        return true;
    }

    /**
     * Este método retorna a EntityManager usada no DAO.
     * @return Objeto do tipo EntityManager.
     */
    public EntityManager getEntityManager() {
        return em;
    }

    public List consultarNativo(String sql) {
        return em.createNativeQuery(sql).getResultList();
    }

    public void refreshObjeto(Object object) {
        em.refresh(object);
    }
}

Alguem tem idéia de como contornar o problema?

Não gostaria de ter que ficar desconectando e conectando de novo do banco de dados.

Abraços
Douglas Junior

Pq vc nao faz o flush depois do commit?

Aprendi utilizar dessa forma, não sabia que poderia fazer flush() depois do commit(), vou testar aqui e já posto os resultados.

Abraços
Douglas

Aprendi utilizar dessa forma, não sabia que poderia fazer flush() depois do commit(), vou testar aqui e já posto os resultados.[/quote]
Eita!

Cuidado com isso, você pode ferrar seu código/banco de dados se não souber para que serve um comando.

O flush força a escrita no banco de dados de toda função pendente.

Fica esperto com os comandos que te falam para utilizar.

^^

Não funciona flush() depois do commit(), tentei para Inserir, Alterar e Remover.

veja:

javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper.throwCheckTransactionFailedException(EntityTransactionWrapper.java:113)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper.checkForTransaction(EntityTransactionWrapper.java:50)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.checkForTransaction(EntityManagerImpl.java:1611)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:696)
at dao.DAO.remove(DAO.java:102)

se não me engano o commit ja chama o flush internamente

[quote=jakefrog]
Eita!

Cuidado com isso, você pode ferrar seu código/banco de dados se não souber para que serve um comando.

O flush força a escrita no banco de dados de toda função pendente.

Fica esperto com os comandos que te falam para utilizar.

^^[/quote]

Eu sei para que o flush() funciona, e sempre soube que deveria ser utilizado antes do commit(), estou utilizando em base de dados de testes e não tem problema se algo der errado.

Porém estou com este problema de remover uma Entidade do banco de dados e ela continua aparecendo na consulta até que eu me desconecte do banco e conecte novamente.

Abraços
Douglas Junior

Pelo que eu saiba não.
Aqui por exemplo fala que depende de quem implementa mas que sua função é apenas de enviar para o banco de dados os comandos que estão em cache.

Como ficou seu método remover então? Posta só ele aí, e quem chama esse método?

cara, eu sempre ouvi que nunca é bom deixar uma conexão com o banco aberta.

se 6000 mil usuários resolverem abrir uma conexão, e todas ficarem aberta você está ***.

abre e fecha sempre que der.

Sério, eu nunca vi o flush ser utilizado antes do commit. Tem algum material que você viu isso? Queria saber o por que.

Pessoal

Acho que resolvi meu problema.

Utilizei o método refresh() do EntityManager para atualizar o objeto, pelo oque li, o método refresh() força o JPA buscar novamente os dados no banco de dados, e assim os dados não são encontrados e o objeto para de aparecer nas consultas.

Obrigado pelas respostas de quem tentou ajudar.

Abraços
Douglas Junior

[quote=ssh]cara, eu sempre ouvi que nunca é bom deixar uma conexão com o banco aberta.

se 6000 mil usuários resolverem abrir uma conexão, e todas ficarem aberta você está ***.

abre e fecha sempre que der.[/quote]

Meu sistema não é web, nuca vai existir 6 mil conexões abertas.

Abraços
Douglas junior

Num li em nenhum documentação, os exemplos que achei na Internet estão assim, e quando vi um pouco sobre JPA na faculdade também utilizava deste jeito.

Lembro de ja ter tentado utilizar o flush() fora do intervalo entre begin() e commit() e não funcionar.

Abraços
Douglas junior

Pelo que eu saiba não.
Aqui por exemplo fala que depende de quem implementa mas que sua função é apenas de enviar para o banco de dados os comandos que estão em cache.

Como ficou seu método remover então? Posta só ele aí, e quem chama esse método?[/quote]

http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html_single/#d0e1429
3.10.1. In a transaction

Flush occurs by default (this is Hibernate specific and not defined by the specification) at the following points:

before query execution*
from javax.persistence.EntityTransaction.commit()*
when EntityManager.flush() is called*
(*) if a transaction is active

Pelo que eu saiba não.
Aqui por exemplo fala que depende de quem implementa mas que sua função é apenas de enviar para o banco de dados os comandos que estão em cache.

Como ficou seu método remover então? Posta só ele aí, e quem chama esse método?[/quote]

http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html_single/#d0e1429
3.10.1. In a transaction

Flush occurs by default (this is Hibernate specific and not defined by the specification) at the following points:

before query execution*
from javax.persistence.EntityTransaction.commit()*
when EntityManager.flush() is called*
(*) if a transaction is active[/quote]
Exato. Por isso falei:

[quote=douglasjunior]Pessoal

Acho que resolvi meu problema.

Utilizei o método refresh() do EntityManager para atualizar o objeto, pelo oque li, o método refresh() força o JPA buscar novamente os dados no banco de dados, e assim os dados não são encontrados e o objeto para de aparecer nas consultas.

Obrigado pelas respostas de quem tentou ajudar.

Abraços
Douglas Junior
[/quote]

Interessante, mas ao mesmo tempo é bem estranho você ter que dar um refresh em algo que foi excluido do banco

Ola Pessoal

Estou revivendo o tópico para deixar alguns esclarecimentos e dizer como resolvi meu problema.

Em relação a questão do flush que a gente estava discutindo, ele é responsável somente por “forçar” o envio das informações do cache do JPA para o banco, isso eu não tenho problemas, pois eu já utilizava o flush e os dados são gravados no banco normalmente.

Meu problema inicial era o seguinte: Tenho dois PCs conectados no mesmo banco via JPA, quando o PC01 faz uma edição de um registro e o PC02 faz uma Query, o registro não vem editado, ele vem conforme a última Query executada pelo PC02, ou seja, a Query não busca os dados modificados no banco, ele mostra sempre o que já está no cache do JPA.

Descobri algumas maneiras de “forçar” o JPA a ir sempre buscar os dados atualizados no banco, uma delas é dar um “refresh” no objeto, assim:

// dessa forma ele busca todos os dados do Objeto novamente no banco de dados.
entityManager.refres(obj)

A outra maneira é fazer o JPA limpar o cache antes de executar a Query, assim:

// dessa forma todos os Objetos em cache do JPA são limpados, e quando executar a query todos serão buscados no banco novamente.
entityManager.getEntityManagerFactory().getCache().evictAll();
entityManager.clear();

E assim eu resolvi meu problema.

Agradeço a atenção de todos que responderam.

Abraço.
Douglas Junior

Só uma pergunta, o método clear já não fazia isso ñ?

Não cheguei fazer testes a fundo só com o clear(), li algumas coisas na Internet e consegui chegar a esse resultado que funcionou como eu precisava.

No Javadoc deles está assim:

entityManager.getEntityManagerFactory().getCache().evictAll();  //Clear the cache.
entityManager.clear();  //Clear the persistence context, causing all managed entities to become detached. Changes made to entities that have not been flushed to the database will not be persisted.

Abraços
Douglas Junior