4
respostas

Tempo de duração de uma transação no wildfly

Quando utilizo o tomcat, e faço um entityManager.find(Produto.class, 1), é aberta uma transação com o banco de dados.

Caso eu faça um get para algum atributo anotado como Lazy, o Hibernate utiliza a mesma transação pra recuperar as informações do BD.

Dessa forma, o código abaixo funciona normalmente:

Produto produto = entityManager.find(Produto.class, 1); System.out.println(produto.getAtributoLazy().getDescricao());

Porém, ao utilizar o WildFly, o mesmo código não funciona. Aparece o erro velho erro de carregamento lazy (failed to lazily initialize... could not initialize proxy - no Session).

Qual seria o "problema"?

4 respostas

Você precisa adicionar fetch=FetchType.EAGER dentro de suas anotações @ManyToMany para puxar automaticamente as entidades filhas

E aí Otávio! Tudo bem?

Na verdade, o carregamento Eager não seria desaconselhado?

Veja que se eu tiver um sistema de controle de pedidos, e o usuário quiser listar os pedidos, na tela de listagem de pedidos ele não precisa necessariamente que os itens de pedido sejam carregados.

Citei isso como exemplo apenas para indicar que, ao colocar o carregamento eager direto no mapeamento, este carregamento será feito em todos os casos em que o objeto for construído, e eu não terei essa necessidade em todos os casos.

Quando colocamos um ERP como escopo do assunto, a coisa ainda fica pior. Tenho um software em que algumas classes possuem muitos atributos, e alguns desses atributos possuem vários outros, de forma que se eu não anotar tudo como lazy, rapidamente a memória do servidor é totalmente consumida.

Dessa forma, uma saída legal é anotar tudo como lazy e entender o funcionamento do hibernate a fundo para saber quando se pode aproveitar a mesma transação, quando se pode utilizar o cache de primeiro nível, e quando seria melhor utilizar o cache de segundo nível.

Nesse caso em específico, aprendi que quando se faz um entityManager.find(NomeClasse.class, idEspecifico), se logo em seguida você fizer um get pra algum atributo anotado como lazy, o hibernate utiliza a mesma transação para buscar essas informações no BD.

E isso funcionava normalmente no tomcat.

Porém, no wildfly não funciona.

Gostaria de entender o motivo.

Ainda fiz um chute, um palpite, e procurei a respeito do tempo em que uma transação fica aberta no wildfly pra comparar com o tempo em que ela fica aberta com a utilização de CDI em um método @Produces e rodando no tomcat.

Mas acabei não encontrando muita coisa. Somente a configuração para estabelecer manualmente o tempo em que as transações ficam abertas no wildfly, no standalone.xml (coordinator-environment), mas sem parâmetro de comparação com o que ocorre no CDI+Tomcat, posso correr o risco de deixar transações abertas por mais tempo que deveria, o que pode causar uma queda de performance na minha aplicação, se visualizarmos um ambiente de um SaaS com múltiplos usuários simultâneos.

Então, como vcs têm bastante experiência com o wildfly, e como é o assunto do curso, pensei em perguntar por aqui. ;)

Olha só que interessante... Fiz a seguinte alteração no projeto do curso:

Na classe AgendamentoEmail, acrescentei um atributo de uma entidade de teste que criei e o anotei com Lazy Loading, da seguinte forma:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "entidade_teste_id")
private EntidadeTeste entidadeTeste;

No DAO, acrescentei um método getPorId, da seguinte forma:

    public AgendamentoEmail getPorId(Long id) {
        AgendamentoEmail agendamentoEmail = entityManager.find(AgendamentoEmail.class, id);
        System.out.println("Descrição da entidade teste: " + agendamentoEmail.getEntidadeTeste().getDescricao());
        return agendamentoEmail; 
    }

Perceba que estou utilizando o mesmo código do projeto, e que em teoria eu deveria estar na mesma transação, e por isso o código acima deveria executar normalmente, sem erros (levando-se em consideração que existe o campo com o ID referenciado).

Porém, ao executar esse método, o seguinte erro aparece:

Caused by: org.hibernate.TransientObjectException: Proxy [br.com.alura.entidade.EntidadeTeste#1] is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session.

E o curioso é que esse código funciona normalmente em um projeto Hibernate com CDI e rodando no Tomcat.

Gostaria de saber mais sobre esse comportamento, e como eu posso fazer para aproveitar o benefício de o Hibernate recuperar campos anotados como Lazy enquanto ainda estiver na mesma transação (sem anotar o atributo como eager, e sem acrescentar um join fetch pra ele trazer tudo de uma vez).

Em outras palavras, se funciona normalmente no tomcat, como eu poderia fazer para funcionar também no wildfly?

Consegui resolver configurando o infinispan no persistence.xml, segue o código:

<property name="hibernate.cache.region.factory_class" value="infinispan"/>
<property name="hibernate.javax.cache.uri"
                      value="org/infinispan/hibernate/cache/commons/builder/infinispan-configs-local.xml"/>

Também tive que configurar o pool no standalone.xml, como demonstra o código a seguir:

<pool>
  <max-pool-size>8</max-pool-size>
</pool>

Porém, fica a Dúvida 1: Por que utilizando o tomcat e sem configurar o pool de conexões, funcionava normalmente, e utilizando o wildfly foi necessário configurá-lo?

Dúvida 2: O problema, na verdade, foi resolvido apenas em parte, porque se eu der um get logo depois do entityManager.find (ainda no DAO), o erro de lazy loading não aparece mais. Porém, quando eu faço o entityManager.find no DAO e faço o getAtributoLazy no Controller, aí o erro volta a aparecer.

Ao que tudo indica, a questão é o tempo em que a transação fica aberta ao se utilizar o o WildFly, que é muito curta.

Ainda tentei utilizar a reconfiguração do timeout, como orientado em https://ralph.blog.imixs.com/2018/10/25/ejb-transaction-timeout-in-wildfly/ , mas sem sucesso. O que de fato era para acontecer, pois o timeout é a tolerância do tempo de espera para que algum erro seja gerado, o que é diferente aumentar propositalmente o tempo em que uma transação com o BD fica aberta.

Bom, mas de qualquer forma, resolvendo essa parte, é um problema a menos.

Agora partimos para a Dúvida 3: No livro "Aplicações Java para a web com JSF e JPA", da Casa do Código, é orientada a utilização do padrão Open Entity Manager in View, através de um Filter.

O livro até fala sobre a utilização de um servidor de aplicações, mas o exemplo presente nele cria o EntityManager no filter, o que só acontece em um Servlet Container, já que em um servidor de aplicações, geralmente, é o próprio servidor quem cria as entity managers. E esta é a Dúvida 3.

Então, resumindo:

Dúvida 1: Por que um get, logo após um entityManager.find, em um atributo lazy, funciona normalmente com CDI + Tomcat, e no WildFly é necessário configurar um pool de conexões para dar certo?

Dúvida 2: Por que, mesmo configurando o pool de conexões, o get logo após o entityManager.find funciona, porém, se eu retirar o mesmo get do DAO e o colocar num Controller, já não funciona? Obs: no CDI + Tomcat funciona normalmente.

Dúvida 3: Como utilizar o padrão Open Entity Manager in View em conjunto com o WildFly, onde o servidor de aplicações gerencia as transações?

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software