[英]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.