3

We have been developing a web site for about a year now, using Spring 4, Hibernate, repositories and JPA. We use the OpenEntityManagerInViewFilter to keep sessions open when retrieving data via endpoints, but recently have been working with scheduled actions and are now running into problems.

We have one method in one class with a @Scheduled annotation, and another method in another class with a @Transactional annotation. The calling method finds data on the file system and passes it to the second class/method.

Here's a simplified version of the code:

<code>
// metadataRepository 
    @Transactional
    public Metadata findOneById(String uuid);


    @Transactional
    public void processQueueMessages(Map<String, MessageAttributeValue> attributes ) throws IOException{
        Metadata metadata = null;
        try{
            metadata = metadataRepository.findOneById(uuid);

            // REST call to get list of locations
            GeotaggerList locationList = response.getResult();

            // this call just maps fields, no database interaction
            Map<String, GeoLocation> locationNameMap = metadataService.getKwebLocations(locationList, null);
            for (Map.Entry<String, GeoLocation> entry : locationNameMap.entrySet()){
                entry.getValue().setMetadata(metadata);
                metadata.getGeoLocations().add(entry.getValue());  //fails
            }
            metadataService.save(metadata, username);
            }
        } catch (Exception e){
            e.printStackTrace();
            return;
        }
</code>

The method/class that calls processQueueMessages has a @Scheduled annotation. I tried adding a @Transactional annotation on the caller, to no avail.

I've tried a few variants of placing the annotations, but always get this error: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: .domain.Metadata.geoLocations, could not initialize proxy - no Session

I can't do an eager fetch in the definition of the Metadata domain object because it's used a lot of places where we don't want eager loading.

I'm open to suggestions.

Randy Avis
  • 75
  • 9

3 Answers3

1

After much searching, the answer was pretty simple, as it usually is. I use a configuration class, and it needed an annotation of @EnableTransactionManagement

The answer was in the spring data documentation http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html

I must have read that page a dozen times, but missed the key statement.

Randy Avis
  • 75
  • 9
0

You could try using hibernate fetch profiles.

https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/performance.html#performance-fetching-profiles

Basically, you keep the same lazy loading by default on your model, but create a fetch profile which eagerly loads your geolocation property. Then when you load the metadata from your scheduled method, activate your fetch profile.

olambert
  • 1,075
  • 2
  • 7
  • 10
0

It seems that metadata object is already out of transactional session after its retrieval from metadataRepository, otherwise there is no point in throwing lazy exception. Check that metadataRepository.findOneById(uuid) is not running in a separate transaction, e.g. by marking it as REQUIRES_NEW

In order to prevent the lazy loading exception, you should preload the data inside call to metadataRepository.findOneById either by simply calling metadata.getGeoLocations().isEmpty() or by using a fetch join query to retrieve data eagerly

Community
  • 1
  • 1
OndroMih
  • 7,280
  • 1
  • 26
  • 44