Spring - 使用OpenSessionInViewFilter为新线程提供Hibernate会话

时间:2015-07-15 19:43:58

标签: java multithreading spring hibernate spring-mvc

我有一个基于Spring 4.0和Hibernate 4的项目,特别是Spring MVC。

对于控制器中的每个请求,OpenSessionInViewFilter正在创建Hibernate会话。

现在,我试图在控制器的方法中启动一个新的线程(做一个漫长的过程)。显然,OpenSessionInViewFilter在请求完成后关闭会话。然后,当我的线程开始时,再也没有会话了,我收到了这个错误:

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

这是类的基础结构,从Controller到我的Callable组件。 IReportService扩展了Callable。

OBS:我已经尝试使用spring的@Async注释,但它仍然无法正常工作。我把REQUIRES_NEW放在服务上试图获得一个新的交易,但它甚至没有改成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;
    }
}

2 个答案:

答案 0 :(得分:1)

你不能在不同的线程中使用相同的hibernate会话 - 它不是线程安全的。所以你不应该担心OpenSessionInViewFilter,即使它不会关闭会话,它们仍然无法从其他线程中使用(并且隐式延迟加载会使它绝对不可预测)。

会话绑定到线程 - 当您尝试从另一个线程访问它时,问题不在于没有会话,这是因为会话从未出现过。

您可以将chVl()与服务方法一起使用,然后从其他线程中的长时间运行的进程中调用这些方法。

P上。 S.同样避免使用@Transactional它们是非常罕见的情况,当你需要回滚内部事务和你的数据库(它应该是一个非常复杂的RDBMS)支持这种行为时 - 它更像是复杂的事务脚本< / i>体系结构比经典POJO驱动的Spring应用程序。

对那些认为hibernate会话是线程安全的非信徒来说,

更新 - 来自Hibernate Session java doc的引用:

  

实现者不应该是线程安全的。而是每一个   thread / transaction应该从a获取自己的实例   会话工厂。

有人发布了关于hibernate会话长对话的答案 - 是的,这个东西存在,但它只适用于单个线程环境(如带有事件循环的SWT应用程序) - 所以你可以在桌面应用程序中使用单个会话,因为它使用单个线程来处理用户输入,但它永远不会在服务器环境中工作。

其他一些答案警告您从不同的线程访问相同的会话

另请注意,着名的Java Persistence with Hibernate提醒您不要在不同的主题中使用相同的会话(与会话工厂相比):

在Hibernate的背景下。 56:

  

在大多数Hibernate应用程序中,SessionFactory应该是   在应用程序初始化期间实例化一次。单身   然后,实例应该被特定进程中的所有代码使用,并且   应该使用这个SessionFactory创建任何Session。该   SessionFactory是线程安全的,可以共享;一个会话是一个   单线程对象

在JPA p。的背景下。 74:

  
      
  • javax.persistence.EntityManagerFactory - 相当于Hibernate SessionFactory。此运行时对象表示一个   特定的持久性单元。它是线程安全的,通常是处理的   作为单身人士,并提供创建方法   EntityManager实例。

  •   
  • javax.persistence.EntityManager-相当于Hibernate Session。 此单线程,非共享对象表示一个
      数据访问的特定工作单元。

  •   

答案 1 :(得分:0)

您遇到问题的原因是您需要了解默认情况下会话的范围与事务范围相同。

在Spring中,事务的范围受限于一个线程,所以如果你打开新线程,他们就无法访问tx,从此以后就不会有会话了。

重要的是要记住,会话可以跨事务(也就是对话)进行扩展,您可以保持会话通过,例如,几个tx甚至不同的HTTP请求。它在Java Persistence with Hibernate

中得到了极好的解释

A session can be expanded through threads

作为个人意见,我永远不会使用OpenSessionInViewFilter,需要仔细处理事务,并且将tx绑定到请求是您的不良问题的常见来源。