【问题标题】:Spring - Provide Hibernate session for a new Thread using OpenSessionInViewFilterSpring - 使用 OpenSessionInViewFilter 为新线程提供休眠会话
【发布时间】:2015-10-05 00:56:32
【问题描述】:

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

Hibernate 的会话由OpenSessionInViewFilter 为控制器中的每个请求创建。

现在,我正在尝试在控制器的方法中启动一个新线程(执行一个漫长的过程)。显然,OpenSessionInViewFilter 正在请求完成后关闭会话。然后,当我的线程启动时,不再有会话,我收到此错误:

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

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

OBS:我曾尝试使用 spring 的 @Async 注释,但它仍然无法正常工作。我将 REQUIRES_NEW 放在 Service 上试图获取新事务,但它甚至更改为 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;
    }
}

【问题讨论】:

    标签: java multithreading spring hibernate spring-mvc


    【解决方案1】:

    您不能在不同的线程中使用相同的休眠会话 - 这不是线程安全的。因此,您不必担心 OpenSessionInViewFilter,即使它不会关闭会话,它们仍然无法从其他线程中使用(并且隐式延迟加载将使其绝对不可预测)。

    会话绑定到线程 - 当您尝试从另一个线程访问它时,问题不是不再有会话,而是因为会话从未存在过。

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

    P。 S. 还要避免使用(propagation = Propagation.REQUIRES_NEW),当您需要回滚内部事务并且您的数据库(它应该是一个非常复杂的 RDBMS)支持这种行为时,它们是非常罕见的情况 - 它是关于复杂的 事务脚本 架构优于经典 POJO 驱动的 Spring 应用程序。

    更新对于那些认为休眠会话是线程安全的非信徒 - Hibernate Session java doc 的引用:

    实现者并不是线程安全的。而是每个 线程/事务应该从一个 会话工厂。

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

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

    另外请注意,著名的Java Persistence with Hibernate 告诫您不要在不同的线程中使用相同的会话(与会话工厂相反):

    在 Hibernate 的上下文中 p。 56:

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

    在 JPA p 的上下文中。 74:

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

    • javax.persistence.EntityManager - 相当于休眠会话。 这个单线程、非共享对象代表一个
      数据访问的特定工作单元。

    【讨论】:

    • 我对您的评论投了反对票,原因有两个:1)您可以在我的回答中公开的不同线程中使用相同的会话。 2)在任何情况下,是否线程安全都不是他的问题的原因,会话是事务有界的,Spring 中的事务是线程有界的,因此是他的问题。
    • @JoséCarlos 感谢您的评论,因此请指出矛盾之处。
    • “矛盾”是你说“你不能在不同的线程中使用相同的休眠会话”-> 这是不正确的。此外,“会话绑定到线程”-> 从技术上讲,这是不正确的。 tx 是线程有界的。不过没有假装打扰。
    • 还有交易绑定到休眠会话,反之亦然!
    • @JoséCarlos 我已经更新了答案,谢谢。 “会话绑定到线程” - 这在 Spring 上下文中是正确的,因为在 Spring Hibernate 会话由 Spring 事务管理器管理,将它们绑定到线程(因为它们不是线程安全的)
    【解决方案2】:

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

    在 Spring 中,事务的范围被限制在一个线程中,因此如果您打开新线程,它们将无法访问 tx,此后,将没有可用的会话。

    请务必记住,会话可以跨事务(也称为对话)扩展,您可以保持会话通过,例如几个 tx 甚至不同的 HTTP 请求。 Java Persistence with Hibernate

    中对它进行了精彩的解释

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

    【讨论】:

    • 是的! Hibernate 支持会话中的长对话,只要它们在单线程中,它可能在单线程桌面应用程序中工作 - 但是 OP 应该在这里和现在做什么?显然他使用了很多线程,你注意到Spring-MVC 标签了吗?晚安!
    猜你喜欢
    • 1970-01-01
    • 2013-12-07
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 2015-04-11
    • 1970-01-01
    • 1970-01-01
    • 2015-09-12
    相关资源
    最近更新 更多