3

I have a project based on Spring 4.0 and Hibernate 4, specifically, Spring MVC.

Hibernate's session is being created byOpenSessionInViewFilter for each request in controller.

Now, I'm trying to start a new Thread inside controller's method (to do a long process). Apparently, OpenSessionInViewFilter is closing the session after request finishes. Then, when my Thread starts, there isn't session anymore and I get this error:

org.hibernate.HibernateException: No Session found for current thread

Here is the basis structure of classes, from Controller to my Callable component. IReportService extends Callable.

OBS: I've been tried to use spring's @Async annotation, but it stills not working. I put REQUIRES_NEW on Service trying to get a new transaction, but it failed even changed to NESTED.

@Controller
@RequestMapping(value = "/action/report")
@Transactional(propagation = Propagation.REQUIRED)
public class ReportController {

    @Autowired
    private IReportService service;
    private final Map<Long, Future> tasks = new HashMap();

    @RequestMapping(value = "/build")
    public String build(@RequestParam Long id) {

        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<StatusProcesso> future = executor.submit(service);

        tasks.put(id, future);

        return "wait-view";
    }

    @RequestMapping(value = "/check", method = RequestMethod.GET)
    public @ResponseBody Map<String, Object> check(@RequestParam Long id) {

        String status = null;

        try {
            Future foo = this.processos.get(id);
            status = foo.isDone() ? "complete" : "building";
        } catch (Exception e) {
            status = "failed";
        }

        return new ResponseBuilder()
                .add("status", status)
                .toSuccessResponse();
    }

    // Another operations...
}


@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class ReportService implements IReportService {

    @Autowired
    private IReportDAO dao;

    @Override
    public Status call() {

        Status status = new Status();

        // do long operation and use DAO...

        return status;
    }
}
clbcabral
  • 503
  • 1
  • 7
  • 23

2 Answers2

3

You can't use same hibernate session in different threads - it's not thread safe. So you should not worry about OpenSessionInViewFilter, even if it would not close the sessions they still would be unusable from other threads(and implicit lazy loading will make it absolutely unpredictable).

Sessions are bound to threads - when you try to access it from another thread the problem is not that there is no session anymore, it's because session has never been there.

You can use @Transactional with the service methods, and then call these methods from the long running processes in other threads.

P. S. Also avoid using (propagation = Propagation.REQUIRES_NEW) they are for very rare case when you need to rollback an inner transaction and your database(it should be a very sophisticated RDBMS) supports such behaviour - it's rather about complex transaction script architecture than classic POJO driven Spring applications.

update for those unbelievers who think that hibernate session is thread safe - a quote from Hibernate Session java doc:

It is not intended that implementors be threadsafe. Instead each thread/transaction should obtain its own instance from a SessionFactory.

someone has posted an answer about hibernate session long conversations - yes this thing exists, but it only works in a single thread environment(like SWT applications with event loop) - so you can use single session in an desktop application because it uses single thread to process user input, but it will never work in the server environment.

Some other answers warning you about accessing same session from different threads

Also please note that famous Java Persistence with Hibernate cautions you against using same session in different thread(in contrast with session factory):

In context of Hibernate p. 56:

In most Hibernate applications, the SessionFactory should be instantiated once during application initialization. The single instance should then be used by all code in a particular process, and any Session should be created using this single SessionFactory. The SessionFactory is thread-safe and can be shared; a Session is a single-threaded object.

In context of JPA p. 74:

  • javax.persistence.EntityManagerFactory—The equivalent to a Hibernate SessionFactory. This runtime object represents a particular persistence unit. It’s thread-safe, is usually handled as a singleton, and provides methods for the creation of EntityManager instances.

  • javax.persistence.EntityManager—The equivalent to a Hibernate Session. This single-threaded, nonshared object represents a
    particular unit of work for data access.

Community
  • 1
  • 1
Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
  • I have vote your comment down for two reasons: 1) You CAN use the same session in different threads as exposed in my answer. 2) Being thread-safe or not is not the reason of his problem in any case, sessions are transaction bounded, transactions in Spring are thread bounded, hence his problem. – José Carlos Jul 15 '15 at 22:28
  • @JoséCarlos thank you for your comment so please point out the contradiction. – Boris Treukhov Jul 15 '15 at 22:28
  • 1
    "The contradiction" is that you say "You can't use same hibernate session in different threads" -> That's not correct. Also, "Sessions are bound to threads" -> Technically speaking, that's not correct. It's tx what is thread bounded. Didn't pretend to bother though. – José Carlos Jul 15 '15 at 22:33
  • And also it's transactions that are bound to hibernate session not vice versa! – Boris Treukhov Jul 15 '15 at 22:41
  • @JoséCarlos I have updated the answer, thanks. "Sessions are bound to threads" - this is correct in Spring context, because in Spring Hibernate sessions are managed by Spring transaction manager which bind them to thread(because they are not threadsafe) – Boris Treukhov Jul 15 '15 at 22:47
  • nice description :) – Peter S. Aug 22 '19 at 13:11
0

The reason of your problem is that you need to understand that by default the scope of a session is the same as the scope of a transaction.

In Spring, the scope of a transaction is bounded to a thread, so if you open new threads, they won't have access to the tx and henceforth, no session will be available.

It is important to keep in mind that sessions can be expanded across transactions (aka conversations), you can keep a session through, e.g. several tx or even different HTTP requests. It is fabulously explained in Java Persistence with Hibernate

A session can be expanded through threads

As a personal opinion I would never use OpenSessionInViewFilter, transactions needs to be tackled carefully and bounding tx to request is a common source of undesired issues such as yours.

José Carlos
  • 414
  • 2
  • 8
  • Yes! Hibernate supports long conversations in session as long as they are in single thread, and it may work in a single threaded desktop application - but what the OP is supposed to do here and now? Obviously he uses many threads, did you notice `Spring-MVC` tag? Good night! – Boris Treukhov Jul 15 '15 at 22:38