[英]Spring webflux: keeping an EntityManager open during the whole process of a reactive request handling
在標准同步 Spring (WebMVC) 世界中,有OpenEntityManagerInViewFilter
和OpenEntityManagerInViewInterceptor
在請求的整個處理過程中保持 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(無阻塞)。
請改用 Spring R2dbc/Spring Data R2dbc,請參閱我的 Spring R2dbc 示例。
使用 Hibernate Reactive,目前官方沒有 Spring/Hibernate Reactive 的集成,但是很容易集成。
persistence.xml
文件。Mutiny.SessionFactory
bean。Mutiny.SessionFactory
處理 RDBMS 操作,請參閱用Hiberante Reactvie編寫的 PostRepository 示例。完整的Spring/Hibernate 反應式示例可以在我的 Github 上找到(但 Web 處理使用的是 Vertx)。
應避免在 Spring Webflux 應用程序中混合阻塞 JPA。 但這是可能的,請參閱我在單個應用程序中混合 JPA 和 R2dbc 的示例。
視圖模式中的 Open session 被設計為在傳統的 servlet 容器中工作,並且依賴於 Spring WebMVC。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.