2

I have an emailsenderservice to manage email notification asynchronously. There are 2 async methods, one method works, but another one throws LazyInitializationException:

@Service
public class EmailSenderService {
    // working
    @Async
    public void sendNewBidRequestEmail(BidRequest bidRequest) {
        this.sendNewBidRequestEmailToSupplier(bidRequest);
    }
    @Transactional
    public void sendNewBidRequestEmailToSupplier(BidRequest bidRequest) {
        sendNewBidRequestEmailToSupplier(bidRequest, bidRequest.getHotels());
    }

    @Transactional
    public void sendNewBidRequestEmailToSupplier(BidRequest bidRequest, List<Hotel> hotelList) {
        for (Hotel hotel : hotelList) {
           ...
           this.sender.send()
        }
    }


    // not working, throw exception
    @Async
    public void sendCancelledBidRequestEmail(BidRequest bidRequest, String reason) {
        this.sendCancelledBidRequestEmailToSupplier(bidRequest, bidRequest.getHotels(), reason);
    }

    @Transactional
    public void sendCancelledBidRequestEmailToSupplier(BidRequest bidRequest, List<Hotel> hotelList, String reason) {
        for (Hotel hotel : hotelList) {   // throw exception here
           ...
           this.sender.send();
        }
}

I'm totally following this thread. You can see both async methods have almost the same structure. Async method calls a transactional method. But the second one throws org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.corpobids.server.entity.BidRequest.hotels, could not initialize proxy - no Session.

I even imitate the first async method structure to modify the second one to:

@Async
    public void sendCancelledBidRequestEmail(BidRequest bidRequest, String reason) {
        this.sendCancelledBidRequestEmailToSupplier(bidRequest, reason);
    }

    @Transactional
    public void sendCancelledBidRequestEmailToSupplier(BidRequest bidRequest, String reason) {
        this.sendCancelledBidRequestEmailToSupplier(bidRequest, bidRequest.getHotels(), reason);
    }

    @Transactional
    public void sendCancelledBidRequestEmailToSupplier(BidRequest bidRequest, List<Hotel> hotelList, String reason) {
        for (Hotel hotel : hotelList) {   // exception in this line
               ...
               this.sender.send();
            }
    }
}

This time, it gives me java.lang.IllegalStateException: org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl@fce9b7b is closed.

I would like to know the missing point in my code, any help would be appreciated.

LunaticJape
  • 1,446
  • 4
  • 21
  • 39

1 Answers1

3

You have issues with JPA.

In JPA the relationships OneToMany has two behaviors LAZY and EAGER.

You can check a good explanation on: Difference between FetchType LAZY and EAGER in Java Persistence API?

I assume when you invoke your code in async mode the context of JPA is loose. So Hibernate can't fill the relationship executing a new query since the context is different. In order to fix your problem you have two options:

  1. Configure the relationship with EAGER
  2. Preload the relationship before invoke async method

Better techniques for lead with Lazy Loading:

https://www.thoughts-on-java.org/5-ways-to-initialize-lazy-relations-and-when-to-use-them/

Ernesto Campohermoso
  • 7,213
  • 1
  • 40
  • 51