2

Some short version infos: Spring Boot 2.1 with Hibernate 5 and Java 8.

We try to do a multithreaded processing step, in which we use spring services to work with hibernate entities. Basically it looks like the following snippet.

   ExecutorService executorService = Executors.newFixedThreadPool(4);


        List<Callable<String>> executions = new ArrayList<>();
        for (String partition : partitions) {
            Callable<String> partitionExecution = () -> {
                step.execute(partition);
                return partition;
            };
            executions.add(partitionExecution);
        }

        executorService.invokeAll(executions);

Problem is that the hibernat session is somehow not available in the created threads. We get the following exception:

org.hibernate.LazyInitializationException: 
failed to lazily initialize a collection of role: ..., could not initialize proxy - no Session

If I remove to multithreading part (i.e. remove the executor service) everyhting works fine.

We already tried the following:

  • Use a spring managed ThreadPoolTaskExecutor
  • Put @Transactional at the top of the method/class (which is wired from another class and invoked there, so should basically work)

Any hints/suggestions appreciated :)

1
  • 2
    Where does step come from and what is it? You will need to show more code I think to get useful feedback. Commented Feb 20, 2020 at 7:33

2 Answers 2

2

I came up with a working version. Basically I let Spring spawn the threads itself using the Async annotation. This way the threads that are created get the wanted hibernate session attached.

I created a spring service that delegates through an async method.

@Component
public AsyncDelegate {

    @Async
    @Transactional
    public Future delegate(Step step, String partition){
        step.execute(partition);
        return new AsyncResult(partition);
    }
}

And adapted the initial code like this:

    @Autowired
    AsyncDelegate asyncDelegate;

    List<Future> executions = new ArrayList<>();
    for (String partition : partitions) {
        executions.add(asyncDelegate.delegate(step, partition));
    }
Sign up to request clarification or add additional context in comments.

Comments

1

In short, I'd strongly advise against passing Hibernate managed objects inside multiple threads as this can cause:

  1. Lazy initialization problems (as you encountered)
  2. Missing updates
  3. Locking exceptions

For more details, this blog post explains why: https://xebia.com/blog/hibernate-and-multi-threading/

For a solution, I'd find a way to split the work so that each thread would get list of entity id's to work with, and they'd do their independent work inside thread (fetch the entities from database, do the actual work etc.). This is briefly mentioned in the above blog post.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.