Reputation: 1044
In a Spring Boot 2.1.2 project with spring.jpa.open-in-view=true, we are (after a couple of years not having this kind of issue) being faced with a LazyInitializationException upon calling a lazy collection.
The stack trace with JPA debugging (logging.level.org.springframework.orm.jpa=debug) indicates that, as expected, the session is not being closed during the execution of the request, independently of individual transactions being committed:
2021-03-22 23:37:25.198 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(211207390<open>)]
2021-03-22 23:37:25.199 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Not closing pre-bound JPA EntityManager after transaction
2021-03-22 23:37:25.199 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(211207390<open>)] for JPA transaction
2021-03-22 23:37:25.199 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
2021-03-22 23:37:25.200 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@6f7a9974]
2021-03-22 23:37:25.200 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit
2021-03-22 23:37:25.201 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(211207390<open>)]
2021-03-22 23:37:25.202 DEBUG 22668 --- [io-8020-exec-10] o.s.orm.jpa.JpaTransactionManager : Not closing pre-bound JPA EntityManager after transaction
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.opsfactor.planning.model.domain.cluster.produto.ClusterProdutos.regrasAlocacaoClusterProdutos, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:597)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:216)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:576)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:147)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
at com.opsfactor.planning.model.projection.configuration.parametros.ClusterEParametrosProjection.getClusterProdutosDeMaterial(ClusterEParametrosProjection.java:740)
Even though the session is not closed the error shows up, always at the same point. The entity and code being executed are:
@Entity
public class ClusterProdutos implements Serializable {
...
---------> collection generating error
@OneToMany(mappedBy = "clusterProdutos",cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Set<RegraAlocacaoClusterProdutos> regrasAlocacaoClusterProdutos = new HashSet<>();
...
}
I have not found any posts/comments on this kind of exception happening with spring.jpa.open-in-view=true, so more than just solving the issue I would like to understand the reason behind it. Probably EAGER fetching the collection or just making everything transactional will make the code run, but it will not answer the question.
So far we have tried to increase the connection timeout, idle timeout, max lifetime and maximum pool size - to no effect.
The error occurs both in my Windows machine and our AWS production server, which runs on their Linux flavour and the database is MySQL 8
UPDATE:
The error stops when I comment out the repository call below, which is made before the instantiation of the previous entity:
@Repository
public interface ProductionPlanLinhaRepository extends JpaRepository<ProductionPlanLinha,ProductionPlanLinhaCompositeKey> {
...
@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true) // https://www.baeldung.com/spring-data-jpa-modifying-annotation
@Query("UPDATE ProductionPlanLinha ppl "
+ "SET ppl.quantidadeSugestaoProducaoBaselineAtendida = ppl.quantidadeSugestaoProducaoBaseline, "
+ "ppl.quantidadeSugestaoProducaoItensNovosAtendida = ppl.quantidadeSugestaoProducaoItensNovos, "
+ "ppl.quantidadeSugestaoProducaoUpliftAtendida = ppl.quantidadeSugestaoProducaoUplift, "
+ "ppl.quantidadeSugestaoProducaoAjusteDemandaAtendida = ppl.quantidadeSugestaoProducaoAjusteDemanda, "
+ "ppl.quantidadeSugestaoProducaoAjusteSupplyAtendida = ppl.quantidadeSugestaoProducaoAjusteSupply "
+ "WHERE ppl.productionPlanLinhaCompositeKey.supplyPlan.id = :supplyPlanId")
public void resetPlanoProducaoRestritoBySupplyPlanId(Long supplyPlanId);
...
}
Any ideas as to how we can keep the update query? Cache reset is a must, as it is not updated with the query.
Upvotes: 0
Views: 1741
Reputation: 944
I added the @Transactional(readOnly = true) to the businees layer method and it fixed it.
/**
* findUserById
* @param id
* @return
*/
@Transactional(readOnly = true)
@Override
public UserDTO findUserById(Long id) {
Upvotes: -1
Reputation: 1148
Actually the issue is not related to Session
here, it's the modifying query which clears the persistence context and the object you are holding upto your web layer, is a detached object now.
If you check the class in error message AbstractPersistenceCollection
it has this function which is called to check if session
is there or not
protected boolean isConnectedToSession() {
return session != null
&& session.isOpen()
&& session.getPersistenceContextInternal().containsCollection( this );
}
As you can see this is the &&
condition, the last condition is checking if persistence context contains the collection and this might be throwing error in your case irrespective of session is open or not.
Upvotes: 2