java - Spring Boot 在关闭时关闭 hibernate session - 在 @Async 方法完成之前

标签 java spring asynchronous spring-boot spring-data-jpa

我有一个 Spring Boot 应用程序的问题,它在 @Async 之前关闭时关闭 EntityManager/session|任务(使用 EntityManager 的)已完成。

有 2 个类与此问题相关:

调度器

预定方法保留有限数量的作业并调用 @Async方法在 XYJobProcessor那做实际的工作。

@Component
public class XYJobProcessingTimer {

    private final XYJobService      xyJobService;
    private final XYJobProcessor    xyJobProcessor;

    //constructor skipped

    @Scheduled(initialDelayString = "${initial_delay}", fixedDelayString = "${delay}")
    public void performXYJobProcessing() {
        final String ticket = UUID.randomUUID().toString();
        final int reservedJobs = xyJobService.findAndReserveReadyXYJobs(ticket);

        if (reservedJobs > 0) {
            final Collection<XYJob> xyJobs = xyJobService.readReservedJobs(ticket);
            xyJobProcessor.process(xyJobs);
        }
    }

}

异步处理器

@Async 注释方法调用访问 EntityManager 的服务。
@Service
public class XYJobProcessor {

    private final XYJobService  xyJobService;

    // constructor skipped

    @Async("xyJobProcessorExecutor")
    public void process(final Collection<XYJob> jobs) {
        // This service uses the EntityManager and takes some time depending o job count.
        xyJobService.createXYJobsAndDelete(jobs);
    }

}

配置
Executor的配置运行 @Async任务。创建的线程是非守护进程。
@Configuration
public class AsyncExecutorConfiguration {

    @Bean(name = "xyJobProcessorExecutor")
    public Executor xyJobProcessorExecutor() {
        final SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
        executor.setConcurrencyLimit(10);
        executor.setThreadNamePrefix("Hasselhoff-");
        return executor;
    }

}

问题

当我关闭应用程序时,spring 会立即关闭 EntityManager session - 在所有 @Async 之前任务已经完成。这导致以下异常:
2017-08-31 16:10:54.212 ERROR 12663 --- [Hasselhoff-12] .a.i.SimpleAsyncUncaughtExceptionHandler : Unexpected error occurred invoking async method 'public void de.xy.services.XYJobProcessor.process(java.util.Collection)'.org.springframework.orm.jpa.JpaSystemException: Session is closed!; nested exception is org.hibernate.SessionException: Session is closed!
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
        at de.xy.services.XYJobService$$EnhancerBySpringCGLIB$$6b9cb1ae.createXYJobsAndDelete(<generated>)
        at de.xy.services.XYJobProcessor.process(XYJobProcessor.java:24)
        at de.xy.services.XYJobProcessor$$FastClassBySpringCGLIB$$ccc40c8f.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at org.springframework.core.task.SimpleAsyncTaskExecutor$ConcurrencyThrottlingRunnable.run(SimpleAsyncTaskExecutor.java:268)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.hibernate.SessionException: Session is closed!
        at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:132)
        at org.hibernate.internal.SessionImpl.getPersistenceContext(SessionImpl.java:2088)
        at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:340)
        at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
        at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282)
        at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:465)
        at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2963)
        at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2339)
        at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:485)
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
        at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
        at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:61)
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
        ... 17 common frames omitted

有没有办法让Spring等待@Async执行要在关闭 EntityManager 之前完成?或者这是一个错误?

最佳答案

感谢@M.Deinum,我发现了如何避免这个问题:

使用 ThreadPoolTaskExecutor (而不是 SimpleAsyncTaskExecutor )并将其设置为 awaitTerminationSecondswaitForTasksToCompleteOnShutdown 相结合的属性(property)设置为 true解决了我的问题。

来自 setAwaitTerminationSeconds 的 JavaDoc :

Set the maximum number of seconds that this executor is supposed to block on shutdown in order to wait for remaining tasks to complete their execution before the rest of the container continues to shut down. This is particularly useful if your remaining tasks are likely to need access to other resources that are also managed by the container.



这是我的问题的确切描述和解决方案。

关于java - Spring Boot 在关闭时关闭 hibernate session - 在 @Async 方法完成之前,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46050522/

相关文章:

Java Spring LDAP 导航到树中

spring - Grails 持久化 Spring 的 "Async"带注释的方法不起作用

javascript - 在 Node Js 中使用异步函数比使用同步函数有什么性能优势吗?

java - RxJava onError 回调中未捕获 InterruptedException?

java - CPU 使用率在 grizzly http (REST) 服务器上经常达到 100% - JAVA8

java - 无法配置JPA项目

java - 为什么 Spring 注解和 xml 配置注入(inject)集合的方式不同?

java - 如何在 Spring xml 中向 Camel http-balancer 添加转换?

Phpmailer 和异步发送电子邮件

macos - Netbeans 在 Mac OS X Mountain Lion 的视网膜显示屏上看起来很模糊