簡體   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