Spring - provide Hibernate session to new thread using OpenSessionInViewFilter

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

A Hibernate session is created OpenSessionInViewFilter

for every request in the controller.

Now I am trying to start a new Thread method inside the controller (to make a long process). Apparently OpenSessionInViewFilter closes the session after the request completes. Then when my Thread starts there is no more session and I get this error:

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

Here is the basic structure of the classes, from the controller to my Callable component. IReportService extends Callable.

OBS: I tried to use Spring annotation @Async

but it still doesn't work. I put REQUIRES_NEW

on Service trying to get a new transaction but it didn't change even 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;
    }
}

      

+3


source to share


2 answers


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

In Spring, the scope of transactions is thread-limited, so if you open new threads, they won't have access to the tx, and the session won't be available later on.

It is important to remember that sessions can be extended through transactions (aka conversations), you can conduct a session through, for example, multiple tx or even different HTTP requests. This 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 have to be carefully resolved and the tx limit for the request is a common source of unwanted issues like yours.

0


source


You cannot use the same hibernate session on different threads - it is not thread safe. Therefore, you shouldn't worry about OpenSessionInViewFilter, even if it doesn't close sessions, they will still be unusable from other threads (and implicit lazy loading will make it completely unpredictable).

Sessions are thread-bound - when trying to access it from another thread, the problem is not that the session no longer exists, because the session never existed.

You can use @Transactional

using service methods and then call those methods from long running processes in other threads.

P.S. Also avoid using (propagation = Propagation.REQUIRES_NEW)

, they are for the very rare case where you need to rollback an internal transaction and your database (it must be a very complex RDBMS) supports this behavior - it's more of a complex transaction script than classic Spring POJO applications.

update for those non-believers who think the hibernate session is thread safe - quote from the Hibernate java doc session:

Developers are not supposed to be thread safe. Instead, each thread / transaction must get its own instance from the SessionFactory.

someone posted an answer about long hibernate session sessions - yes this thing exists, but it only works in a single thread environment (e.g. SWT applications with event loop) - so you can use a single session in a desktop application as it uses one thread to handle user input, but it will never run in a server environment.



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

Also note that the famous Java Persistence with Hibernate warns you against using the same session on a different thread (as opposed to a factory session)

In the context of Hibernate c. 56:

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

In the context of JPA c. 74:

  • javax.persistence.EntityManagerFactory is the Hibernation equivalent of SessionFactory. This runtime object represents a specific persistence unit. Its thread-safe, typically handled like a singleton, and provides methods for creating EntityManager Instances.

  • javax.persistence.EntityManager - Hibernate session equivalent. This single-threaded, untainted object represents a concrete unit of work for data access.

+1


source







All Articles