Objetos de Entidade de BD do JPA não atualiza, o que fazer?

63 respostas Resolvido
javahibernate
AndreSI

Olá pessoal!

estou desenvolvendo um sistema com swing e JPA.
tenho minhas classes geradas a partir das tabelas do BD e o programa funciona assim:

  • Clico em um botão da janela e um conteúdo do Banco de dados é mostrado no centro dela em uma JTable .

  • O problema ocorre quando eu modifico um dado no BD (um nome de um funcionário, por exemplo) e mesmo quando limpo todos os componentes da janela e clico para exibir os dados novamente, eles continuam os mesmos. Parece que os objetos JPA não são atualizados.

  • E no meu código sempre que o botão é cllicado é criada uma nova instancia, mas não está atualizando os dados do BD.

Qual o problema e a Solução???

63 Respostas

javaflex

Usar JPA/Hibernate é uma grande aventura. Depende de como está implementado. Certifique se está abrindo uma nova session a cada ação do usuário, senão ele vai pegar do cache mesmo.

AndreSI

OLÁ,
sou novo nessa aventura e o que vc quer dizer com “abrindo uma nova session”?
vc se refere a fechar e abrir um novo EntityManagerFactory a cada acao?

javaflex

Na época que usava Java tinha o SessionFactory (instancia que deve ser única, alocado na aplicação a vida toda). Com ele chamava o método openSession para abrir a conexão com o banco. Seja lá qual for o “comando” ou mágica hoje, cada vez que o usuário realizar uma ação no sistema, a conexão com o banco de dados deverá ser aberta e fechada assim que terminar de retornar os dados pro usuário. Dependendo de como estiver implementado, se manter a session (não SessionFactory) aberta a vida toda corre o risco dele ler do cache ao invés do banco de dados.

AndreSI

Aí, consegui usando esses métodos usados sobre o EntityManagerFactory a cada listagem e antes de fechá-lo;

Model.FACTORY.getCache().evictAll();
fJpaC.getEntityManager().clear();

Model.FACTORY.close();

javaflex

Isso funciona, mas evict é pra ser usado em último caso em situações anormais. Provavelmente está usando cache sem necessidade, deixando a conexão sempre aberta, ou algo que vá ficar encoberto e sendo remediado por evicts.

Curioso que cache quando realmente necessário é um dos poucos motivos para se usar JPA/Hibernate, a não ser que esteja usando vários SGDBs ao mesmo tempo.

D

Bom não sei se entendi muito bem seu problema, mas segue um exemplo de como eu realizo as atualizações… na minha classe dao.

`

Session sessao = HibernateUtil.getSessionFactory().openSession();

Transaction transacao = null;
try {
		transacao = sessao.beginTransaction();
		sessao.update(funcionario);

		transacao.commit();
	} catch (RuntimeException ex) {
		if (transacao != null) {
			transacao.rollback();
		}
		throw ex;
	} finally {
		sessao.close();
	}`

lembrando que após realizar um update ou alguma outra coisa, tem que realizar o update do table via ajax. :smiley:

javaflex

Exatamente, essa é a forma normal de se trabalhar, em que não seria necessário ficar usando evict.

AndreSI

A atualização que eu me refiro, Deivid, é uma consulta (um select usando JPA). O problema é que essa consulta mostra sempre os mesmos dados, ainda que eu modifique os dados no BD usando o phpmyadmin, por exemplo. Aí quando clico pra listar os dados novamente ele não muda.
vou verificar e aplicar o código que vcs me passaram e informo o resultado sem usar evict.

ah e o meu app é desktop, não uso ajax.

javaflex

Se você fizer como o exemplo que ele passou, sendo que para consulta, continua o problema?

Session sessao = sessionFactory.openSession();

Query query = sessao.createQuery("from Cliente where id = :id ");

query.setParameter("id", "123");

List resultado = query.list();

//....passa o resultado pra um DTO, ModelView ou que for que esteja usando pra servir de interface pra atender a UI.......

sessao.close();
pfk66

E não são mesmo. A ferramenta não é feita pra funcionar dessa maneira, onde você altera o banco por fora do JPA.

AndreSI

Olá gente, agradeço muito a ajuda de vcs. Encontrei o meu erro!
Como eu estou entrando agora nessa onda de JPA desconhecia que sempre temos que criar uma nova EntityManagerFactory a cada interação com o BD e sempre fechá-la. Eu usava um EntityManagerFactory único em uma variável static por achar que cada abertura deixaria o programa pesado, mas não.
Eis aki o meu código SEM ERROS.

public class PainelListaDeFuncionarios extends javax.swing.JPanel implements ActionListener{

private final DefaultTableModel modelo1;

private final JanelaPrincipal janelaPai;
List<Funcionario> funcionarios = null;
EntityManagerFactory fctr;
public PainelListaDeFuncionarios(JFrame janelaPai) {

this.janelaPai = (JanelaPrincipal) janelaPai;

initComponents();

modelo1 = (DefaultTableModel) tabelaFuncs.getModel();
fctr = Persistence.createEntityManagerFactory("com.sigef_jar_1.0-SNAPSHOTPU");
    FuncionarioJpaController fJpaC =
            new FuncionarioJpaController(fctr);
    
    funcionarios = fJpaC.findFuncionarioEntities();
    
    int numRow = modelo1.getRowCount();
    Object[] line; 
    Funcionario funcion;
    
    for (int idx = 0; idx < funcionarios.size(); idx++) {
        funcion = funcionarios.get(idx);  
        
        line = new Object[]{
            funcion.getPessoa().getPessoaPK().getId(),
            funcion.getPessoa().getNome(),
            funcion.getCargo().getNome(),
            funcion.getPessoa().getNascimentoData()
        };
        modelo1.addRow(line);            
    }
    
  
    
    //IMPORTANTE PARA FECHAR AS CONEXÕES DO BD
    fctr.close();

}

}

temos que sempre abrir com o createEntityManagerFactory() e fechar. Erro grosseiro! Mas valeu a experiência. Obrigado gente!
agora meu software vai ficar perfeito.

javaflex

Pois é, mas já que insistem em usar JPA/Hibernate sem sem ter a necessidade dessa coisa toda de cache, então ele vai ter que reabrir a conexão a cada consulta ou usar StatelessSession. Ele está usando uma ferramenta que está atrapalhando mais do que ajudando, ou se ao menos tivesse justificado o uso, como por exemplo multibanco.

javaflex
Solucao aceita

Parabéns por considerar a solução ideal (considerando se realmente vai querer usar Hibernate).

AndreSI

Curioso como muitas coisas em java só aprendemos dessa forma, errando e consultando fóruns!!
:smile::smile:

pfk66

Errado. O JavaDoc do EntityManagerFactory diz:

When the application has finished using the entity manager factory, and/or at application shutdown, the application should close the entity manager factory. Once an EntityManagerFactory has been closed, all its entity managers are considered to be in the closed state.

AndreSI

Eita verdade, verdade!

não fechei o EntityManagerFactory e funcionou da mesma forma! Agora tenho que criar apenas um EntityManagerFactory e usá-lo sempre em todas as classes e fechar apenas quando a aplicação for fechada?

pfk66

Mas não existe essa opção de usar JPA mas não “toda essa coisa de cache”. rs

javaflex

Achei que tivesse fechado só a session como o exemplo que passamos. O factory não é para fechar não! Factory é super pesado deve existir uma única instância em todo a vida da aplicação como já explicado no início.

javaflex

Não entendi o que você quis dizer.

AndreSI

Assim como está no código que eu postei, nem chego a usar essa session.
uso apenas EntityManagerFactory

javaflex

Como você está usando EntityManagerFactory, acredito que você deveria usar:

EntityManager entityManager = entityManagerFactory.createEntityManager();

Esse entityManager deve ser o equivalante a “session” que falamos para sempre abrir e fechar a cada requisição do usuário.

JPA parece ser mais confuso ainda do que Hibernate puro. Vocês são guerreiros em usar.

AndreSI

Sim, EntityManager eu sempre uso e fecho em cada método do CRUD, alíás faço o netbeans gerar as classes do controlador JPA.
exemplo…

private List findFuncionarioEntities(boolean all, int maxResults, int firstResult) {

EntityManager em = getEntityManager();

try {

CriteriaQuery cq = em.getCriteriaBuilder().createQuery();

cq.select(cq.from(Funcionario.class));

Query q = em.createQuery(cq);

if (!all) {

q.setMaxResults(maxResults);

q.setFirstResult(firstResult);

}

return q.getResultList();

} finally {

// em.flush();

em.close();

}

}
javaflex

Ai realmente não sei o que pode ser. Com Hibernate puro não tinha esses problemas e podia inclusive trabalhar com Session sem cache.

pfk66

O problema é que ele está alterando o BD pelo myadmin, ou seja, por fora do JPA e a ferramenta não foi feita pra funcionar dessa forma.

javaflex

Sim, eu já concordei com isso. Tem que ficar reabrindo a conexão, pelo menos no Hibernate puro, já JPA parece que o buraco é mais enrolado ainda.

pfk66

Na verdade a especificação JPA nem trata de cache.
Isso é um detalhe de implementação do provedor de persistência.

pfk66

Não. A solução é parar de alterar o BD por fora do ORM, ou então parar de usar ORM.

javaflex

Sim, mas na prática ele está tendo problema em ler de cache ao invés de banco.

AndreSI

Mas por hora o meu problema já está SOLUCIONADO. Resumindo o que eu queria era:

  1. clicar no botão “listar funcionários” e listar os dados deles numa tabela;
  2. limpar a tabela attravés de outro botão;
  3. alterar os dados por fora (pelo phpmyadmin, aliás isso pode ser necessário caso a aplicação funcione em rede);
  4. clicar NOVAMENTE no botão “listar funcionários” e listar os dados ALTERADOS;
    Antes não estava funcionando, mas agora depois que eu crio um EntityManagerFactory e fecho funciona, Obrigado pela ajuda javaflex e pfk66.
pfk66

Seu problema está solucionado em ambiente de desenvolvimento com 1 usuário. Em produção com vários ao mesmo tempo e outra história.

ps: no caso, como se trata de uma aplicação desktop, 1 usuário é normal. É só a interface do usuário não deixar ele realizar várias operações que envolvem o banco ao mesmo tempo, ou vai travar a aplicação por estar criando vários factories.

javaflex

Mas esse é o problema que ele quer resolver, senão ele nem teria criado esse post. Independente da má escolha da ferramenta, é o que ele quer.

AndreSI

JPA é uma má escolha?

pfk66

Se você precisa ficar atualizando o BD pelo myadmin, sim. Por que você vai ter que saber como o mecanismo d cache funciona e recarregar as entidades, e isso é um detalhe de implementação do provedor de persistência, e não do JPA.

javaflex

Se o seu projeto não é multibanco e você precisa dos dados sempre vindos online do banco, JDBC puro com SQL nativo é mais indicado, mais leve, mais sob controle. Se é multibanco e não precisa de cache, pelo menos com Hibernate puro tinha liberdade pra usar StatelessSession.

AndreSI

Ok, Acho que entendi.
Então imaginem o meu software java com JPA (desktop) funcionando em rede:
2 pc’s

  1. O pc A é clicado pra pra exibir todos os funcionarios do BD;
  2. O usuário do pc B edita os dados de um funcionário;
  3. O usuario do pc A clica pra exibir os funcionários novamente e já aparece os dados alterados pelo pc B.

Isso vai funcionar né?

Eu estou usando o phpmyadmin pra poder editar dados enquanto eu ainda não desenvolvi a parte que insere ou edita dados na minha aplicação.

pfk66

“Mas esse é o problema que ele quer resolver, senão ele nem teria criado esse post. Independente da má escolha da ferramenta, é o que ele quer.”

Se ele ficar reabrindo a conexão vai ter outros problemas, então obviamente não pode ser a solução. E JPA não possui stateless session.

javaflex

Não é a solução que eu e você gostaríamos, mas é para os casos que insistem em usar essas ferramentas mesmo nessas situações, e acontece bastante no mercado infelizmente. O nome da tecnologia é “Hibernar”! E JPA realmente limita mais, mas muitos preferem seguir padrões mesmo sem motivos reais. Felizmente Hibernate pra .NET está livre desse tipo de padrão.

javaflex

Depende de como estiver implementado, como já foi explicado aqui sobre as saídas pra isso.

pfk66

Não vai funcionar com JPA porque pc A e B possuem diferentes caches e um não tem como saber que o estado foi alterado pelo outro. Você vai ter que reiniciar a aplicação pra ver as alterações surtirem efeito.

AndreSI

Nossa!
se JPA não tem uma solução pra isso e apenas ele preenche objetos com dados do BD uma única vez, sinceramente não sei pra que ele serve

javaflex

Se está usando a implentação do Hibernate isso vai ocorrer se não abrir uma nova session (não factory) para cada requisição do usuário. Agora se o JPA não permite isso, não sei informar, nunca passei por motivo real pra usar padrão JPA, Hibernate puro já passei por projeto usando sim e sem esses problemas, por abrir uma nova session a cada requisição do usuário ou por usar StatelessSession.

pfk66

Não é solução pra ninguém. Por isso está no Javadoc como que deve ser feito.

javaflex

Pode não ser para nós, e provavelmente para ele não deveria ser também. As vezes isto está acima de razões, envolve religião também, a Sun infelizmente plantou muito isso pregando especificações padrões, e quem vai pro mercado fica refém.

pfk66

Serve pra você trabalhar com objetos Java, sem ter que manipular tabelas diretamente no phpmyadmin, e no contexto onde o banco de dados é acessado por apenas uma aplicação (monolitico).

Se você quer compartilhar os dados com várias aplicações, o melhor seria expor o banco como um serviço em vez de objetos.

javaflex

@AndreSI Você já tentou usar o EntityManager como falei acima?

Segue um exemplo mais completo:

EntityManager em = seuEntityManagerFactory.createEntityManager();
try {
    Query query = em.createQuery("from Cliente");
    return query.getResultList();
}
finally {
    em.close();
}
AndreSI

@javaflex o meu problema já foi SOLUCIONADO lá em cima com o código abaixo e nas minhas classes DAO já utilizo códigos como esse que vc postou. Enfim consegui que o JPA me trouxesse objetos atualizados a partir do BD, abrindo e fechando o EntityManagerFactory conforme o código abaixo.

public class PainelListaDeFuncionarios extends javax.swing.JPanel implements ActionListener{

private final DefaultTableModel modelo1;

private final JanelaPrincipal janelaPai;

List funcionarios = null;
EntityManagerFactory fctr;

public PainelListaDeFuncionarios(JFrame janelaPai) {

this.janelaPai = (JanelaPrincipal) janelaPai;

initComponents();

modelo1 = (DefaultTableModel) tabelaFuncs.getModel();
fctr = Persistence.createEntityManagerFactory(com.sigef_jar_1.0-SNAPSHOTPU);

FuncionarioJpaController fJpaC =

new FuncionarioJpaController(fctr);

funcionarios = fJpaC.findFuncionarioEntities();

int numRow = modelo1.getRowCount();

Object[] line;

Funcionario funcion;

for (int idx = 0; idx < funcionarios.size(); idx++) {
funcion = funcionarios.get(idx);

line = new Object[]{

funcion.getPessoa().getPessoaPK().getId(),

funcion.getPessoa().getNome(),

funcion.getCargo().getNome(),

funcion.getPessoa().getNascimentoData()

};

modelo1.addRow(line);

}

//IMPORTANTE PARA FECHAR AS CONEXÕES DO BD
fctr.close();

}
}

cada vez que esse JPanel é instanciado na minha janela principal quando clico no botão ele cria o EntityManagerFactory e puxa os dados novos do BD.

pfk66

Se desabilitar o cache do hibernate, vai ter problema de performance porque cada join no SQL é uma viagem ao banco. Agora o desenvolver da aplicação tem que ser DBA e se preocupar em gerar queries otimizadas…

Enfim, a essa altura os benefícios do ORM já foram por água a baixo.

javaflex

Não sou defensor de Hibernate/JPA, mas é possível fazer joins e gerar um único SQL.

pfk66

É possível pra quem? desenvolvedores? DBAs?

javaflex

Acredito que isso não seja boa prática.

AndreSI

Como assim? a que estás se referindo?
é dessa forma que nos recomendam, veja:
caelum.com.br/apostila-java-web/uma-introducao-pratica-ao-jpa-com-hibernate/

javaflex

É possível fazer joins com Hibernate e ele executar um único SQL e não sempre um para cada join, fica a cargo do desenvolvedor.

javaflex

EntityManagerFactory carrega as configurações mapeadas, imagina ficar toda hora carregando configurações? Se considera isso boa prática então tudo bem.

Veja bem nessa linha, ele cria o EntityManager como tinha te falado:

EntityManager manager = factory.createEntityManager();

cviniciusm

Olá,

Sabem que o Weld que é a implementação do CDI, permite usar CDI com Java SE ?

Vejam: http://stackoverflow.com/questions/20935977/what-is-the-easiest-way-to-have-cdi-and-jpa-in-java-se e https://docs.jboss.org/weld/reference/latest/en-US/html/environments.html#_java_se
"
When executing in the SE environment the following features of Weld are available:

  • Managed beans with @PostConstruct and @PreDestroy lifecycle callbacks
  • Dependency injection with qualifiers and alternatives
  • @Application, @Dependent and @Singleton scopes
  • Interceptors and decorators
  • Stereotypes
  • Events
  • Portable extension support
    "

Assim usa aplicação Java SE pode ficar mais robusta ! :wink:

AndreSI

Mas eu crio e uso o EntityManager ele está imbutido no método findFuncionarioEntities() daqui:

FuncionarioJpaController fJpaC = new FuncionarioJpaController(fctr);
funcionarios = fJpaC.findFuncionarioEntities();

parece que vcs ainda não usaram o netbeans pra gerar classes de entidade e classes controladoras com JPA.

Vou continuar meu projeto depois eu volto aki.

javaflex

Netbeans? Não mesmo. Fazia tudo na mão pra saber o que estou fazendo.

AndreSI

ou eclipse. o eclipese tbm faz.
agente Começa fazendo à mao mesmo, porém depois que entendenmos, devemos usar essas ferramentas pra agilizar. principlamente se o projeto for robusto. Lembrem se do manifesto ágil !!

javaflex

Ágil seria entregar por partes, não um projeto robusto. Mas se está dando certo pra você, tudo bem, não existe solução única.

pfk66

É possível fazer isso sem Hibernate tb. lol

Mas ferramentas como Hibernate foram criadas justamente para que o desenvolvedor não tenha que fazer isso, ou seja, não ter que pensar em termos de tabelas, SQL, e como escrever queries otimizadas. Em teoria você apenas navegaria pelo objeto e suas associações.

javaflex

Sim, acredito que já tenha ficado claro que não defendo usar Hibernate nestes casos, é complicação desnecessária, além de consumir recurso em vão. Só te expliquei que é possível.

AndreSI

Eita mais uma coisa pra eu saber, se tá sendo complicado JPA, com mais essa de CDI então!

:grin::grin::smile:

pfk66

altera os dados no phpmyadmin > info não aparece nos objetos > desliga o cache > problema de performance > desenvolvedor deixa de de desenvolver a aplicação e a UI pra otimizar query.

É possível, mas pelo menos uns 5 graus de separação do problema original.

pfk66

Como o javaflex disse, não é a melhor pratica, mas como é desktop, apenas 1 usuário…

Se fosse um web server, vc teria que aproveitar o mesmo EntityManagerFactory, senão seu servidor ia pro saco rapidinho.

Criado 12 de outubro de 2016
Ultima resposta 15 de out. de 2016
Respostas 63
Participantes 5