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

63 respostas Resolvido
javahibernate
A

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

J

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.

A

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?

J

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.

A

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

J

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:

J

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

A

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.

J

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

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

A

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.

J

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.

J
Solucao aceita

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

A

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

P

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.

A

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?

P

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

J

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.

J

Não entendi o que você quis dizer.

A

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

J

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.

A

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

}

}
J

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.

P

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.

J

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.

P

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

P

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

J

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

A

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.
P

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.

J

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.

A

JPA é uma má escolha?

P

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.

J

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.

A

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.

P

“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.

J

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.

J

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

P

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.

A

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

J

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.

P

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

J

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.

P

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.

J

@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();
}
A

@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.

P

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.

J

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

P

É possível pra quem? desenvolvedores? DBAs?

J

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

A

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/

J

É 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.

J

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

C

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:

A

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.

J

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

A

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 !!

J

Á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.

P

É 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.

J

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.

A

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

:grin::grin::smile:

P

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.

P

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