繁体   English   中英

Spring webflux:在响应式请求处理的整个过程中保持 EntityManager 打开

[英]Spring webflux: keeping an EntityManager open during the whole process of a reactive request handling

在标准同步 Spring (WebMVC) 世界中,有OpenEntityManagerInViewFilterOpenEntityManagerInViewInterceptor在请求的整个处理过程中保持 JPA EntityManager打开,并允许避免烦人的LazyInitializationException (“无会话”)。

对于基于异步 servlet API 的应用程序, OpenEntityManagerInViewInterceptor也以类似的方式工作。

但是在 Webflux 应用程序中如何处理这个问题呢? 假设我有一个 Webflux 控制器,它可以执行类似的操作

service.getOneReactively(...).flatMapMany(one -> obtainAFlux(one))

其中service.getOneReactively()在数据库中找到一个域对象,并obtainAFlux()访问该域对象上的延迟集合,从而导致其加载。 这两个调用都将在某个线程池上执行,可能在不同的线程上执行,因此绑定到第一个线程(域对象被具体化的地方)的 Hibernate Session将无法在第二个线程(加载集合的地方)中访问。

这会导致LazyInitializationException

你如何解决这样的问题? 到目前为止,我唯一能发明的就是将两个调用打包到一个方法中,并在一个响应式调用中的事务中调用它。

Hibernate不是被动的,它使用JDBC(这是一个阻塞的数据库驱动程序),并依次使用threadlocal来存储会话信息,这在Webflux中是无法使用的。

如果希望阻止数据库调用,则必须使用Mono.fromCallable并将调用分配给自己的Scheduler,以便它获得自己的专用线程。 您可以在文档https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking休眠不能自然返回Flux的文档中了解更多信息。

您的代码应如下所示

Flux<Objects> myObjects = Mono.fromCallable(() -> {

    // make all your calls to the database here
    // build then your flux or whatever you want

    return Flux.fromIterable(listOfObjects);
}).subscribeOn(Schedulers.elastic()); // Makes sure this is run in its own thread

正如@Toerktumlare 提到的,Hibernate 不是响应式的,它使用 JDBC,它是一个阻塞数据库驱动程序,但您可以使用Hibernate 响应式并使用非阻塞方法和sessions 一个简单的代码示例必须是这样的:

public Flux<MyUser> findAll() {
    try {
        Mutiny.SessionFactory factory = CustomEntityManagerFactoryPostgresImpl
                .getInstance()
                .getEntityManagerFactory()
                .unwrap(Mutiny.SessionFactory.class);

        CriteriaBuilder builder = factory.getCriteriaBuilder();
        CriteriaQuery<MyUser> criteria = builder.createQuery(MyUser.class);
        Root<MyUser> root = criteria.from(MyUser.class);

        Flux<MyUser> myUser = Flux.fromIterable(
            factory.withSession(
                    session -> session
                            .createQuery(criteria)
                            .getResultList()
            ).await().indefinitely());
        factory.close();
        return myUser;
    } catch (Exception exception) {
        log.error(String.valueOf(exception));
        return Flux.empty();
    }

一个mono<>就像:

public Mono<MyUser> getOneUser(Long id) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence");
    Mutiny.SessionFactory sessionFactory = emf.unwrap(Mutiny.SessionFactory.class);
    Mutiny.Session session = sessionFactory.openSession();

    try {
        return Mono.fromFuture(session.find(MyUser.class, id).subscribe().asCompletionStage());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

应避免在 WebFlux 应用程序中使用阻塞式 JPA API。

如果您想在 Spring WebFlux 应用程序中访问 RDBMS,请尝试改用响应式流 API(无阻塞)。

  1. 请改用 Spring R2dbc/Spring Data R2dbc,请参阅我的 Spring R2dbc 示例

  2. 使用 Hibernate Reactive,目前官方没有 Spring/Hibernate Reactive 的集成,但是很容易集成。

    • 创建一个persistence.xml文件。
    • 创建一个Mutiny.SessionFactory bean。
    • 使用Mutiny.SessionFactory处理 RDBMS 操作,请参阅用Hiberante Reactvie编写的 PostRepository 示例。

    完整的Spring/Hibernate 反应式示例可以在我的 Github 上找到(但 Web 处理使用的是 Vertx)。

  3. 应避免在 Spring Webflux 应用程序中混合阻塞 JPA。 但这是可能的,请参阅在单个应用程序中混合 JPA 和 R2dbc 的示例

视图模式中的 Open session 被设计为在传统的 servlet 容器中工作,并且依赖于 Spring WebMVC。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM