Problema com persistência Hibernate + WildFly

138 views
Skip to first unread message

Everton Patricio Pereira

unread,
Nov 20, 2021, 9:35:17 AM11/20/21
to ce...@googlegroups.com
Olá meus caros! Tudo bem?

Galera, tenho enfrentado o seguinte problema:

1.Tenho uma classe com alguns lists mapeados da seguinte forma: @OneToMany(mappedBy="nfeTransporte", cascade = CascadeType.ALL, orphanRemoval = true)

2. Quando preencho o objeto normalmente, na primeira vez, e o salvo, o Hibernate persiste normalmente no BD;

3. Se dou um "merge" várias vezes sequencialmente, ele também persiste normalmente;

4. No entanto, se eu passo algum tempo (2 min, por exemplo) para dar um segundo merge, aparece o seguinte erro ==> A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance

5. Já pesquisei bastante, e só encontro referências como o link abaixo, dizendo que esse erro é originado por conta de preenchimento inadequado dos lists. Fiz igual ao orientado, mas continuou com o mesmo erro. De qualquer forma, entendo que se fosse esse o problema, o objeto não seria persistido logo no primeiro merge.
https://betterjavacode.com/java/a-collection-with-cascadeall-delete-orphan-was-no-longer-referenced-by-the-owning-entity-instance

6. Como entendo que o cache de primeiro nível do Hibernate tem um tempo limitado, então dou chutar que talvez tenha a ver com isso. Por outro lado, não era pra ter a ver com isso, pq se eu tenho um objeto preenchido, e quero fazer um merge, era pra ele persistir no BD e refletir os dados do objeto. Não é verdade?

Gostaria de ouvir a opinião de vcs...

Desde já, muito obrigado!

Everton Patricio

Roberto Badaró

unread,
Nov 20, 2021, 10:15:55 AM11/20/21
to cejug
Olá, Everton.

Você pode compartilhar o seu código para podermos avaliar? Não se esqueça de apontar o trecho onde ocorre a "pausa" prolongada antes de estourar o erro. O stacktrace também pode ser útil.

Abraço,
Roberto Badaró


--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+un...@googlegroups.com.
---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+un...@googlegroups.com.
Para ver essa discussão na Web, acesse https://groups.google.com/d/msgid/cejug/984837834.100578.1637418911649%40mail.yahoo.com.

Everton Patricio Pereira

unread,
Nov 20, 2021, 11:06:29 AM11/20/21
to ce...@googlegroups.com
Sim, certeza Roberto. Muito obrigado pelo retorno! :D

Vou colocar o código de uma classe menor pra facilitar o entendimento, já que o problema ocorre em todas as classes independentemente da quantidade de atributos.

//Código no Controller
    // Métodos principais
    public void salvar() {
        Participante participanteRetornado = new Participante();
        participanteRetornado = participanteService
                .salvar(participanteSelecionado);

        if (participanteRetornado.getId() != null) {
            this.participanteSelecionado = participanteService.buscarObjetoCompleto(participanteRetornado.getId());
        }
    }



//Código no Service do método buscarObjetoCompleto
    public Participante buscarObjetoCompleto(Long id) {
        return participanteDao.buscarObjetoCompleto(id);
    }



//Código no DAO do método buscarObjetoCompleto
    public Participante buscarObjetoCompleto(Long id) {

        Session session = em.unwrap(Session.class);
        Criteria criteria = session.createCriteria(Participante.class);

        criteria.add(Restrictions.eq("ativo", true));

        criteria.setFetchMode("setor", FetchMode.JOIN);
        criteria.setFetchMode("municipio", FetchMode.JOIN);
        criteria.setFetchMode("municipio.uf", FetchMode.JOIN);
        criteria.setFetchMode("setorAnterior", FetchMode.JOIN);
        criteria.setFetchMode("municipioAvalista", FetchMode.JOIN);
        criteria.setFetchMode("fase", FetchMode.JOIN);
        criteria.setFetchMode("persona", FetchMode.JOIN);
        
        criteria.add(Restrictions.idEq(id));
        
        Participante participanteRetornar = (Participante) criteria.addOrder(Order.asc("id")).uniqueResult();
        
        if (participanteRetornar != null) {
            if (participanteRetornar.getAcoes() != null) {
                participanteRetornar.getAcoes().size();
            }
            if (participanteRetornar.getEquipamentos() != null) {
                participanteRetornar.getEquipamentos().size();
            }
        }
        
        return participanteRetornar;
    }


Envolvendo essa ação no sistema, esses são os códigos associados. Mas se precisar de mais, pra  melhorar o entendimento, é só falar. :D

Estou rodando o software no WildFly 25.0.1, e as configurações no pom.xml para o Hibernate são essas:

        <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jcache</artifactId>
            <version>5.4.10.Final</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>5.4.10.Final</version>
            <scope>provided</scope>
        </dependency>

Um detalhe que talvez seja interessante: eu faço uma integração com sistema Desktop por meio de um banco de dados, e a cada minuto faço uma verificação no banco de dados através de um Singleton, utilizando o JDBC nativo. Aí pego os dados do BD local através do JDBC (Firebird) e persisto os dados na aplicação Java através do Hibernate ( BD PostgreSQL).

Desde já, muito obrigado!:



Everton Patricio Pereira

unread,
Nov 20, 2021, 11:22:12 AM11/20/21
to 'Everton Patricio Pereira' via CEJUG
Ahh, ficou faltando a questão do erro gerado. Vou colocar aqui abaixo uma parte do trecho gerado pelo WildFly.
Sobre a pausa prolongada, não é do software. É minha mesmo. :D

É que tive a ideia de fazer o merge, e eu mesmo esperar algum tempo para depois fazer o merge novamente e ver o comportamento.

Valeu! :D



Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
    at org.jboss.jts//com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1307)
    at org.jboss.jts//com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:128)
    at org.jboss.jts.integration//com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:94)
    at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.LocalTransaction.commitAndDissociate(LocalTransaction.java:78)
    at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.ContextTransactionManager.commit(ContextTransactionManager.java:71)
    at org.jbos...@25.0.1.Final//org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:91)
    ... 155 more
    Suppressed: javax.transaction.RollbackException: WFTXN0061: Transaction is marked rollback-only
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.setRollbackOnly(AbstractTransaction.java:96)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.LocalTransaction.setRollbackOnly(LocalTransaction.java:159)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.ContextTransactionManager.setRollbackOnly(ContextTransactionManager.java:94)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionAdapterTransactionManagerImpl.markRollbackOnly(JtaTransactionAdapterTransactionManagerImpl.java:100)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl$TransactionDriverControlImpl.markRollbackOnly(JtaTransactionCoordinatorImpl.java:456)
        at org.hi...@5.3.23.Final//org.hibernate.engine.transaction.internal.TransactionImpl.markRollbackOnly(TransactionImpl.java:206)
        at org.hi...@5.3.23.Final//org.hibernate.internal.AbstractSharedSessionContract.markForRollbackOnly(AbstractSharedSessionContract.java:381)
        at org.hi...@5.3.23.Final//org.hibernate.internal.ExceptionConverterImpl.handlePersistenceException(ExceptionConverterImpl.java:297)
        at org.hi...@5.3.23.Final//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
        at org.hi...@5.3.23.Final//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
        at org.hi...@5.3.23.Final//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
        at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1478)
        at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:512)
        at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3310)
        at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2506)
        at org.hi...@5.3.23.Final//org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:447)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.beforeCompletion(JtaTransactionCoordinatorImpl.java:352)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:47)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:37)
        at org.jboss.as...@25.0.1.Final//org.jboss.as.txn.service.internal.tsr.JCAOrderedLastSynchronizationList.beforeCompletion(JCAOrderedLastSynchronizationList.java:113)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:236)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:247)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization.beforeCompletion(AbstractTransaction.java:292)
        at org.jboss.jts//com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:76)
        at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:360)
        at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:91)
        at org.jboss.jts//com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
        at org.jboss.jts//com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1295)
        ... 160 more
    Suppressed: javax.transaction.RollbackException: WFTXN0061: Transaction is marked rollback-only
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.setRollbackOnly(AbstractTransaction.java:96)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.LocalTransaction.setRollbackOnly(LocalTransaction.java:159)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.ContextTransactionManager.setRollbackOnly(ContextTransactionManager.java:94)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionAdapterTransactionManagerImpl.markRollbackOnly(JtaTransactionAdapterTransactionManagerImpl.java:100)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl$TransactionDriverControlImpl.markRollbackOnly(JtaTransactionCoordinatorImpl.java:456)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.beforeCompletion(JtaTransactionCoordinatorImpl.java:359)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:47)
        at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:37)
        at org.jboss.as...@25.0.1.Final//org.jboss.as.txn.service.internal.tsr.JCAOrderedLastSynchronizationList.beforeCompletion(JCAOrderedLastSynchronizationList.java:113)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:236)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:247)
        at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization.beforeCompletion(AbstractTransaction.java:292)
        at org.jboss.jts//com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:76)
        at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:360)
        at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:91)
        at org.jboss.jts//com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
        at org.jboss.jts//com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1295)
        ... 160 more
Caused by: javax.persistence.PersistenceException: org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: pojo.Participante.equipamentos
    at org.hi...@5.3.23.Final//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
    at org.hi...@5.3.23.Final//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
    at org.hi...@5.3.23.Final//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
    at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1478)
    at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:512)
    at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3310)
    at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2506)
    at org.hi...@5.3.23.Final//org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:447)
    at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.beforeCompletion(JtaTransactionCoordinatorImpl.java:352)
    at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:47)
    at org.hi...@5.3.23.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:37)
    at org.jboss.as...@25.0.1.Final//org.jboss.as.txn.service.internal.tsr.JCAOrderedLastSynchronizationList.beforeCompletion(JCAOrderedLastSynchronizationList.java:113)
    at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:236)
    at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:247)
    at org.wildfly.tra...@1.1.14.Final//org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization.beforeCompletion(AbstractTransaction.java:292)
    at org.jboss.jts//com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:76)
    at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:360)
    at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:91)
    at org.jboss.jts//com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
    at org.jboss.jts//com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1295)
    ... 160 more
Caused by: org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: pojo.Participante.equipamentos
    at org.hi...@5.3.23.Final//org.hibernate.engine.internal.Collections.processDereferencedCollection(Collections.java:100)
    at org.hi...@5.3.23.Final//org.hibernate.engine.internal.Collections.processUnreachableCollection(Collections.java:51)
    at org.hi...@5.3.23.Final//org.hibernate.event.internal.AbstractFlushingEventListener.flushCollections(AbstractFlushingEventListener.java:262)
    at org.hi...@5.3.23.Final//org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:95)
    at org.hi...@5.3.23.Final//org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:38)
    at org.hi...@5.3.23.Final//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1472)
    ... 176 more



Everton Patricio Pereira


Roberto Badaró

unread,
Nov 20, 2021, 11:39:18 AM11/20/21
to cejug
Everton,

Para eu entender melhor. O erro só acontece com você fazendo o teste de pausa? No uso normal não dá erro?

Roberto Badaró


Everton Patricio Pereira

unread,
Nov 20, 2021, 11:56:05 AM11/20/21
to ce...@googlegroups.com
Olha que legal: pra responder a sua pergunta, e também me dar outra perspectiva, acabei de fazer outro teste.

Olhando para um cronômetro, passei exatamente três minutos e meio clicando em salvar (dando um merge), a cada cinco segundos, e não apareceu o erro.

Então, logo que deu três minutos e meio, parei de clicar em salvar, e  esperei mais três minutos e meio. Após esse segundo intervalo, cliquei em salvar (dei um merge) e o erro apareceu.

Curioso, não? kkkkkkkkkk...

Outra informação que talvez possa ajudar: estou utilizando o JSF com a implementação fornecida pelo próprio WildFly, como mostra a dependência abaixo:

        <dependency>
          <groupId>javax</groupId>
          <artifactId>javaee-api</artifactId>
          <version>8.0</version>
          <scope>provided</scope>
        </dependency>


Muito obrigado,
Everton Patricio

Roberto Badaró

unread,
Nov 20, 2021, 12:35:56 PM11/20/21
to cejug
Everton,

Existe mais algum outro erro no log, antes do erro que você mandou?
Como está configurado o seu entity manager?
A sua classe service é EJB? Se sim, Stateless ou Stateful?

Estou procurando pistas. Ainda sem uma ideia clara do que pode ser.

Roberto Badaró


Everton Patricio Pereira

unread,
Nov 20, 2021, 1:17:37 PM11/20/21
to ce...@googlegroups.com
Só esse erro mesmo, meu caro.

Meu service é um EJB Stateless, e o EntityManager está configurado com @PersistenceContext.

Segue a configuração do persistence.xml

    <persistence-unit name="ImperadorPU" transaction-type="JTA">
        <jta-data-source>java:jboss/datasources/ImperadorDS</jta-data-source>

        <shared-cache-mode>ALL</shared-cache-mode>

        <validation-mode>CALLBACK</validation-mode>

        <properties>
            <property name="javax.persistence.schema-generation.database.action"
                      value="none"/>

            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="hibernate.hbm2ddl.auto" value="update" />

            <property name="hibernate.show_sql" value="true" />
            <!--   <property name="hibernate.format_sql" value="true" />  -->
            
            <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"/>
            
            
        </properties>
    </persistence-unit>


Muito obrigado! :D

Everton Patricio Pereira


Marcus Mazzo Laprano

unread,
Nov 20, 2021, 1:25:15 PM11/20/21
to ce...@googlegroups.com
Boa tarde.

O teu método de merge é o mesmo de save? Como está localizando o objeto para fazer o merge?

Não entendi o fluxo executado.

Você salva. Ele persiste no banco. 
Depois você faz o merge. Como? Tem uma lista no front que Manda um objeto pro teu ejb que chama o método merge? 

Cumprimentos

Everton Patricio Pereira

unread,
Nov 20, 2021, 3:31:06 PM11/20/21
to ce...@googlegroups.com
Olá Marcus! Tudo bem? Muito obrigado pelo retorno! :D

O método "merge" é do próprio Hibernate, e o método "salvar" foi criado por mim.

Criei métodos com o nome "salvar", e os coloquei no Controller, no Service e no DAO (no DAO, chamei de "salvarAtualizar").

Na página xhtml tem um botão primefaces do qual eu aciono o método "salvar" do controller;

O método "salvar" do controller chama  o "salvar" do service, que por sua vez chama o método "salvarAtualizar" do DAO;

O método "salvarAtualizar" no DAO, chama o "merge", do Hibernate, que persiste o objeto recebido como parâmetro e o retorna, juntamente com o ID gerado pelo banco.
 
.

No controller eu tenho um objeto que é: 1. Preenchido pelo usuário através dos componentes da página xhtml, ou 2. Retornado do banco de dados, caso seja a edição de informações;

Como muitos dos atributos estão configurados com Lazy Loading, por questões de economia de memória e consequente desempenho, para que o usuário possa continuar alterando as informações do objeto mesmo depois de "clicar no botão salvar", que aciona o método salvar, eu adotei a seguinte estratégia:

1. Persisto o objeto no banco de dados através do merge, que retorna apenas os valores que não estão anotados com lazy loading;

2. Pego o ID do objeto, e executo um outro método no DAO, chamado buscarObjetoCompleto, que retorna todos os atributos, mesmo os anotados com lazy loading.

Qualquer coisa, o código dessa sequência está no primeiro e-mail, e qualquer dúvida, é só falar.

Muito obrigado!

Atenciosamente,
Everton Patricio



Everton Patricio Pereira


Marcus Mazzo Laprano

unread,
Nov 20, 2021, 3:39:37 PM11/20/21
to ce...@googlegroups.com
Ok.

O erro de collection normalmente indica que a collection não tem a referência do objeto pai.

Ao fazer o merge tenta verificar, na lista, se os objetos que estão sendo enviados (mesmo não tendo sido atualizados) possuem a referência para o pai. Se não possuir pode ocorrer o erro acima. 

Lembrando que, mesmo em um contexto de ejbs stateless, enquanto o contexto existir alterações efetuadas ao objeto irão refletir na bd.

Outro ponto, quando o objeto vem da bd, verifica se as listas estão inicializadas. Se estiverem precisa verificar se os objetos estão preenchidos e possuem a referência ao pai e se não existir objetos na lista (lista empty) faz set dela para null no momento do merge e garante que não exista referência pra está lista.

Cumprimentos

Everton Patricio Pereira

unread,
Nov 20, 2021, 4:02:34 PM11/20/21
to ce...@googlegroups.com
Informação nova que talvez nos ajude na compreensão do problema: acabei de fazer outro teste com dois objetos diferentes. Persisti dois objetos de classes diferentes no banco de dados, ao mesmo tempo, e obtive os seguintes resultados:

1. No primeiro objeto, persisti-o no banco de dados apenas com o merge do Hibernate, e não apareceu esse problema depois do tempo esperado (10 min);

2. Já no segundo objeto, persisti-o no banco de dados e imediatamente fiz a pesquisa utilizando o método buscarObjetoCompleto, e nesse apareceu o problema que descrevi no início, depois do mesmo período esperado.

Isso talvez indique  que o problema tenha a ver com o método buscarObjetoCompleto e o comportamento do Hibernate.

Para facilitar, segue novamente o código do método:

    public Participante buscarObjetoCompleto(Long id) {

        Session session = em.unwrap(Session.class);
        Criteria criteria = session.createCriteria(Participante.class);

        criteria.add(Restrictions.eq("ativo", true));

        criteria.setFetchMode("setor", FetchMode.JOIN);
        criteria.setFetchMode("municipio", FetchMode.JOIN);
        criteria.setFetchMode("municipio.uf", FetchMode.JOIN);
        criteria.setFetchMode("setorAnterior", FetchMode.JOIN);
        criteria.setFetchMode("municipioAvalista", FetchMode.JOIN);
        criteria.setFetchMode("fase", FetchMode.JOIN);
        criteria.setFetchMode("persona", FetchMode.JOIN);
        
        criteria.add(Restrictions.idEq(id));
        
        Participante participanteRetornar = (Participante) criteria.addOrder(Order.asc("id")).uniqueResult();
        
        if (participanteRetornar != null) {
            if (participanteRetornar.getAcoes() != null) {
                participanteRetornar.getAcoes().size();
            }
            if (participanteRetornar.getEquipamentos() != null) {
                participanteRetornar.getEquipamentos().size();
            }
        }
        
        return participanteRetornar;
    }



Desde já, muito obrigado! :D


Everton Patricio Pereira


Marcus Mazzo Laprano

unread,
Nov 20, 2021, 4:18:50 PM11/20/21
to ce...@googlegroups.com
Como lhe disse.

O objeto da consulta pode estar a trazer referências transientes. Se a lista vem sem objetos dentro então set da lista para null no pai. Se vem com objetos verifique se os objetos dentro da lista possuem referência ao id do pai. Se não existir terá de fazer o set neles.

Ao sair do método, como dito, está em contexto transacional e o hibernate tentará fazer o commit das referências transientes.


Cumprimentos

Marcus Mazzo Laprano

unread,
Nov 20, 2021, 4:48:06 PM11/20/21
to ce...@googlegroups.com
Outro ponto

No teu método salvarAtualizar anota com
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
Isso faz com que ao sair do método de salvar seja feito commit pois, ao entrar no método uma nova transação é criada e ao sair o ejb fara o commit.

Acredito que assim você consegue isolar o problema no teu método que busca os dados e ao sair deste método, como não existe um contexto transacional, não tentará fazer commit 

Cumprimentos

Everton Patricio Pereira

unread,
Nov 20, 2021, 5:01:20 PM11/20/21
to ce...@googlegroups.com
Fiz a seguinte alteração no método salvar do Controller:

    public void salvar() {

        if (participanteSelecionado.getEquipamentos() != null) {
            System.out.println("A lista de equipamentos NÃO está nula.");
            
            System.out.println("Tamanho da lista de equipamentos: " + participanteSelecionado.getEquipamentos().size());
            
        } else {
            System.out.println("A lista de equipamentos ESTÁ está nula.");

        }
        
        Participante participanteRetornado = new Participante();
        participanteRetornado = participanteService
                .salvar(participanteSelecionado);

        if (participanteRetornado.getId() != null) {
            this.participanteSelecionado = participanteService.buscarObjetoCompleto(participanteRetornado.getId());
        }
    }



E em seguida continuei na mesma linha de raciocínio: fiz três testes de persistência seguidos, um após o outro,  esperei três minutos e fiz o quarto.

O retorno de todos os quadro foi idêntico, como segue:

//Retorno dos testes sequenciais
2021-11-20 18:36:15,114 INFO  [stdout] (default task-1) A lista de equipamentos não está nula.

2021-11-20 18:36:15,114 INFO  [stdout] (default task-1) Tamanho da lista de equipamentos: 0


2021-11-20 18:37:16,454 INFO  [stdout] (default task-2) A lista de equipamentos não está nula.

2021-11-20 18:37:16,454 INFO  [stdout] (default task-2) Tamanho da lista de equipamentos: 0


2021-11-20 18:37:35,100 INFO  [stdout] (default task-2) A lista de equipamentos não está nula.

2021-11-20 18:37:35,100 INFO  [stdout] (default task-2) Tamanho da lista de equipamentos: 0


//Retorno do teste separado
2021-11-20 18:40:43,785 INFO  [stdout] (default task-2) A lista de equipamentos não está nula.

2021-11-20 18:40:43,786 INFO  [stdout] (default task-2) Tamanho da lista de equipamentos: 0


Uma outra característica interessante é que essas listas estão sendo inicializadas, logo na sua declaração, com um ArrayList ( private List<Equipamento> equipamentos = new ArrayList<>(); ).

Em um momento, pensei que pudesse ser isso. Então retirei a inicialização na declaração e não preenchi a lista, sento esta ficando nula. Mas ocorreu o mesmo problema.

Em outro momento, tentei outras abordagens, como a citada no site https://codippa.com/how-to-resolve-a-collection-with-cascadeall-delete-orphan-was-no-longer-referenced-by-the-owning-entity-instance/ , mas sem resultado diferente.

.

Agora um fato interessante, é que se eu simplesmente remover o "orphanRemoval = true" da anotação, funciona normalmente, e o erro não aparece.

Mas não quero simplesmente retirar a anotação. Quero entender o funcionamento, o motivo de o erro estar acontecendo, etc, pq assim acredito que acrescento os meus conhecimentos e evoluo como profissional (mesmo dando mais trabalho :D).

.

Muito obrigado!


Everton Patricio Pereira


Everton Patricio Pereira

unread,
Nov 20, 2021, 5:20:42 PM11/20/21
to ce...@googlegroups.com
Fiz conforme orientado, mas acabou ocorrendo o mesmo erro. Coloquei a anotação no método do DAO, ficando assim:


Everton Patricio Pereira


--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+un...@googlegroups.com.
---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+un...@googlegroups.com.

Everton Patricio Pereira

unread,
Nov 20, 2021, 5:22:23 PM11/20/21
to ce...@googlegroups.com
(A mensagem anterior foi incompleta, peço desculpas. Segue a mensagem completa):

Fiz conforme orientado, mas acabou ocorrendo o mesmo erro. Coloquei a anotação no método do DAO, ficando assim:

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public Participante salvarAtualizar(Participante participante) {
        participante.setDataCadastro(new Date());
        return em.merge(participante);
    }




Everton Patricio Pereira


--
-- Você está inscrito na lista de discussão técnica do CEJUG. Para sair da lista de discussão, envie um email para cejug+un...@googlegroups.com.
---
Você recebeu essa mensagem porque está inscrito no grupo "CEJUG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para cejug+un...@googlegroups.com.

Marcus Mazzo Laprano

unread,
Nov 20, 2021, 5:27:29 PM11/20/21
to ce...@googlegroups.com

O ejb efetua controle do contexto transacional. Um objeto inicializado, mesmo sem "nada", ao sair de um método com contexto transacional o ejb fará commit da transação. No teu caso, um objeto com uma lista em branco ao sair do método o ejb vai fazer commit da transação e a lista em branco é transient, ocorrendo o erro.

Tudo se remete ao controle transacional. Ao salvar o retorno do ejb já é um objeto com ID preenchido e este está acoplado a uma sessão, enquanto a sessão existir qualquer alteração no objeto reflete em BD e não seria necessário, logo após, fazer um "findById" por exemplo pois já tem o objeto preenchido. 

Anote o método que faz o merge com a anotação que mandei, de requiresnew, e faça os testes novamente.

Cumprimentos

Marcus Mazzo Laprano

unread,
Nov 20, 2021, 5:29:50 PM11/20/21
to ce...@googlegroups.com
Pois então faz o seguinte.


Tira a tua consulta do método que faz o save e retorna o objeto que veio do save. Ele já possui id, já possui tudo que precisa possuir.

Cumprimentos

Marcus Mazzo Laprano

unread,
Nov 20, 2021, 5:36:23 PM11/20/21
to ce...@googlegroups.com
Me.manda mensagem direto pro meu email, fica mais fácil.


Cumprimentos

Everton Patricio Pereira

unread,
Nov 20, 2021, 5:50:45 PM11/20/21
to ce...@googlegroups.com
Show de bola! Combinado Marcus.

Atenciosamente,
Everton

Everton Patricio Pereira


Rafael Ponte

unread,
Nov 21, 2021, 9:04:48 AM11/21/21
to ce...@googlegroups.com
opa,

o Marcus tá dando duro pra te ajudar, pois fazer troubleshooting sem ver todos os detalhes do código é uma tarefa bem difícil. 

eu chuto que seu problema tem a ver com as múltiplas transações que você está abrindo e fechando. Não que seja um problema de fato, mas pelo que entendi você está misturando uma entidade de um contexto transacional em outro contexto. Se houver relacionamentos lazy não inicializadas pode rolar problema, dado que cada contexto transacional tem sua própria sessão (EntityManager). 

É incomum precisar de uma 2a transação (REQUIRES_NEW). E dependendo de onde ela foi iniciada a coisa pode ficar ainda pior, pois talvez a transação anterior não tenha sido comitada. 

Faz o seguinte, garante que todo o fluxo acontece na mesma transação e nos diga o que acontece. 

Enfim, é um chute dado que não temos uma visão clara de todo o fluxo de execução e de como as entidades são armazenadas (escopos do JSF, aplicação etc). 

--
Rafael Ponte
Engenheiro de Software Cansado

Marcus Mazzo Laprano

unread,
Nov 21, 2021, 9:45:40 AM11/21/21
to ce...@googlegroups.com
Como comentei para o Everton. Tudo se resume ao contexto transacional. Tem algo estranho no que ele está fazendo. Por isso indiquei o requiresnew pra isolar os blocos transacionais e tentar perceber em que momento realmente ocorre erro. Mas realmente é bem complicado perceber algo sem ver o código inteiro.

Enviei a ele um código de exemplo sobre controle de transações com ejbs pra ele tentar perceber o que há de diferente entre os codigos. Mas, assim como você Rafael, eu chuto que tem problema de contexto transacional.

Bom, acredito que o exemplo que mandei para ele tem como ele perceber o que pode estar sendo feito errado.

Cumprimentos

Everton Patricio Pereira

unread,
Nov 23, 2021, 7:58:49 AM11/23/21
to ce...@googlegroups.com
Olá pessoal! Tudo bem com vcs?

Passando pra agradecer pela dedicação e disponibilidade do Marcus em ajudar na resolução do problema, assim como também a disponibilidade do Rafael que sempre está por aqui pra nos ajudar.

Para facilitar (é o que eu deveria ter  feito desde o início), criei uma versão mini do projeto, que reproduzi o erro comentado no início. O link está logo abaixo:




Apenas para relembrar, é uma estrutura relativamente simples: temos um controller (ParticipanteControl), um service (ParticipanteService) e um DAO (ParticipanteDao).

Após os atributos do projeto serem preenchidos e o método "salvar" do controller ser acionado, os dados  são persistidos no BD. E como há atributos configurados como Lazy, o merge não  retorna esses atributos e eu preciso dos mesmos, logo após o merge, executo o método "buscarObjetoCompleto".

Se eu executar o método "salvar" do controller 10 vezes seguidas, nenhum problema acontece.

O problema aparece quando eu executo o método "salvar" do controller, e então espero alguns minutos para depois executá-lo novamente.

Sempre recebo o erro: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: pojo.Participante.equipamentos

Novamente, agradeço a colaboração, disponibilidade e boa vontade de todos, em especial do Marcus e do  Rafael.

Atenciosamente,
Everton Patricio


Marcus Mazzo Laprano

unread,
Nov 23, 2021, 8:41:29 AM11/23/21
to ce...@googlegroups.com
Everton. Peço desculpas, só consigo ver mais pro final de semana, na semana é corrido.

Se até lá você não tiver sucesso eu olhar pra ver o que está pode estar acontecendo.

Abraços

Everton Patricio Pereira

unread,
Nov 23, 2021, 8:48:51 AM11/23/21
to ce...@googlegroups.com
Entendo perfeitamente Marcus, e agradeço muito pela ajuda que vê já tem dado. :D

Caso eu consiga desenrolar durante a semana, aviso por aqui.

Abraços! :D

Everton Patricio Pereira


Davi Mustafa

unread,
Nov 23, 2021, 8:51:35 AM11/23/21
to ce...@googlegroups.com
Everton, olhando aqui o código do teu git, onde tá teu @Transactional?

Não tem nem no service nem no Dao. Ideal para você manter todo o processo na transação, que esteja no service.

Everton Patricio Pereira

unread,
Nov 23, 2021, 9:02:09 AM11/23/21
to ce...@googlegroups.com

Olá Davi! Tudo bem?

Nesse caso, não utilizei o @Transactional porque não é a minha aplicação quem gerencia as transações, mas o próprio servidor de aplicação (WildFly).

Configurei isso na anotação @PersistenceContext do EntityManager do DAO e no transaction-type="JTA" do persitence.xml

Abraços

:D


Everton Patricio Pereira


Davi Mustafa

unread,
Nov 23, 2021, 9:37:32 AM11/23/21
to ce...@googlegroups.com
Mas precisa sim, até para controlar teu contexto transacional. Se você quisesse que um rollback acontecesse em algum ponto do teu código, ou desativar a transação em algum ponto para executar uma stored procedure.

Já chegou a pôr e testar?

Rafael Ponte

unread,
Nov 23, 2021, 10:10:13 AM11/23/21
to ce...@googlegroups.com
Mustafa,

Ele está usando EJB, e todo @Stateless tem por padrão todos os métodos transacionais (como se estivessem anotados com @Transactional) :-)

Davi Mustafa

unread,
Nov 23, 2021, 10:24:15 AM11/23/21
to ce...@googlegroups.com
Boa Rafa,

Tempo sem EJB, já tava perdido aqui. Foi mal Everton.

Everton Patricio Pereira

unread,
Nov 23, 2021, 10:54:53 AM11/23/21
to ce...@googlegroups.com

Tranquilo Davi. :D

Apenas a título de curiosidade, pra vc relembrar, quando não quisermos que um método seja transacional, basta anotá-lo com @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED).

O resto fica com a divisão de responsabilidades entre que vc faz entre DAO e Service.

Ex: se vc tem duas alterações a serem feitas no BD, e estas são independentes, então vc coloca as duas no service, anota o método como mostrado acima, e somente os métodos do DAO ficam sendo transacionais.

Muito obrigado pelo seu feedback! :D



Everton Patricio Pereira

unread,
Nov 23, 2021, 8:18:55 PM11/23/21
to 'Everton Patricio Pereira' via CEJUG
Pessoal, ao que podemos ver nos links abaixo, aparentemente é uma falha no Hibernate:


Dessa forma, acredito que será complicado contornar essa situação. :/

Uma dúvida que seria interessante então, para o caso, seria: qual é a abordagem de vocês em um cenário de aplicação JSF, em que você precisa carregar uma entidade pai e inicializar uma lista lazy (ex: pedido de venda e itens de pedido de venda), e o usuário precisa alterar as informações para posteriormente persisti-las no banco de dados?

Só lembrando que enquanto o usuário estiver utilizando a aplicação, o problema não aparece. Este só aparece quando o usuário passa alguns minutos sem utilizar a aplicação e, posteriormente, decide persistir as informações.

Desde já, muito obrigado! :D

Everton Patricio

Everton Patricio Pereira

unread,
Nov 23, 2021, 10:31:53 PM11/23/21
to 'Everton Patricio Pereira' via CEJUG
Descobri!!!!!! :D :D :D

Há algumas semanas, “perambulando” pela net, descobri o recurso de Bytecode Enhancement.

Adicionei então o plugin no pom.xml pra fins de testes, e por falta de atenção, acabei deixando-o por lá.

Agora entendi que foi só por conta disso que após anos, só vim descobrir esse problema agora: porque o plugin foi adicionado recentemente.

Mas sabe o que é interessante? Na semana passada, eu tinha excluído o plugin e realizado o teste, porém, o erro mesmo assim tinha persistido. Então achei que o Bytecode Enhancement não fosse o responsável.

Então dando uma lida nesse link, deparei-me com esse trecho:




“It might be caused by hibernate-enhance-maven-plugin. When I enabled enableLazyInitialization property this exception started on happening on my lazy collection. I'm using hibernate 5.2.17.Final.

Pensei então: vou fazer um teste mais severo. Além de retirar a referência ao plugin de Bytecode Enhancement do pom.xml, vou retirar o projeto do server no eclipse, vou até a pasta standalone\deployments e excluo o war de lá, vou até a pasta tmp e excluo a referência de lá também, então darei um Maven/Update Project, em seguida um Project/Clean, e por último adiciono o projeto novamente no servidor para então fazer o teste.

Segui todos passos, e deu tudo certo! O problema não apareceu mais.

Recoloquei o plugin, segui os passos citados para atualização, e o problema retornou.

Retirei novamente o plugin, segui novamente todos os passos para a atualização, e o problema desapareceu. :D

Mas acho que foi providência divina eu ter passado bem perto da resposta e solução algumas vezes e não tê-la encontrado: assim pude perturbar um pouco vocês. Kkkkkkkkkkkkk...

Mas é isso aí pessoal, agradeço imensamente o tempo que vocês tiraram para dar uma olhadinha e contribuir com a ajuda, em especial ao Marcus, ao Rafael e ao Davi.

Ahh, e para quem estiver curioso em aprender mais sobre o , basta dar uma olhadinha aqui: Bytecode Enhancement




Abraços e até logo!

Everton Patricio

Reply all
Reply to author
Forward
0 new messages