Java 在AsyncUncaughtExceptionHandler中获取Hibernate会话

Java 在AsyncUncaughtExceptionHandler中获取Hibernate会话,java,spring,hibernate,session,dao,Java,Spring,Hibernate,Session,Dao,当@Async操作因异常而失败时,我想在数据库中创建一个异常日志 您可以在下面看到AsyncExecutorConfiguration和AsyncExceptionHandler类的实现 在AsyncExceptionHandler类中,当我调用试图访问数据库的服务时,我得到:org.hibernate.HibernateException:无法获取当前线程的事务同步会话 @Configuration @EnableAsync public class AsyncExecutorConfigur

@Async
操作因异常而失败时,我想在数据库中创建一个异常日志

您可以在下面看到
AsyncExecutorConfiguration
AsyncExceptionHandler
类的实现

AsyncExceptionHandler
类中,当我调用试图访问数据库的服务时,我得到:
org.hibernate.HibernateException:无法获取当前线程的事务同步会话

@Configuration
@EnableAsync
public class AsyncExecutorConfiguration implements AsyncConfigurer {

    @Autowired
    private AsyncExceptionHandler asyncExceptionHandler;

    private static final int CORE_POOL_SIZE = 3;
    private static final int MAX_POOL_SIZE = 3;
    private static final int QUEUE_CAPACITY = 24;
    private static final String THREAD_NAME_PREFIX = "AsynchThread-";

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return asyncExceptionHandler;
    }

}

@Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    @Autowired
    private NotificationService notificationService;

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = false)
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        AsyncErrorLog log = new AsyncErrorLog(ex);
        notificationService.saveLogAndNotify(log); // throws exception "Could not obtain transaction-synchronized Session for current thread"
    }
}


@Service
public class MyServiceImpl implements MyService {

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void doSomething(Long id) {
        // I can execute database operations here

    }
    ...
@Async
函数本身已具有有效会话。我应该怎么做才能在
AsyncExceptionHandler
类中也有一个有效的会话

--

更新

下面是
notificationserviceinpl
LogDaoImpl.class
的简化实现,我们从中得到了错误

@Service
public class NotificationServiceImpl implements NotificationService {

    @Autowired
    private LogDao logDao;

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = false)
    public void saveLogAndNotify(Log log) {
        return logDao.createLog(log);
    }


@Repository
public class LogDaoImpl{

    @Autowired
    protected SessionFactory sessionFactory;


    @Override
    public void createLog(Log log) {
        sessionFactory.getCurrentSession().saveOrUpdate(log);
    }

根据Hibernate异常;如果不使用Spring数据,则必须确保通知服务在Hibernate会话上显式调用数据库调用

另一方面,根据我的经验,
UncaughtExceptionHandler
(通常)的主要用例用于:

  • 处理
    运行时异常
    的一种简单的最后手段,程序员可能不知道这些异常由于某种原因无法(或没有)在代码中捕获
  • 捕获程序员无法控制的代码中的异常的一种方法(例如,如果您直接在某个第三方库上调用Async,等等)
  • 两者之间的共同点是,这个
    处理程序
    用于一些意想不到的事情。事实上,Spring本身解释了您自己的代码和Spring Async(代码)中的“意外”,让您不必担心流氓异常会杀死线程并且不知道原因。(注意:源代码中的消息说它正在捕获一个“意外”异常。当然,异常是意外的,但这些异常是您真正不知道可能发生的。Spring Async将为您记录。)

    在您的示例中,就是这样,因为您正在执行Spring数据库操作,并且应该确切地知道
    #doSomething
    内部发生了什么,所以我只需要删除AUEH a
    try catch
    (和/或
    -最终
    )并处理
    #doSomething
    内部的异常:

    @Service
    public class MyServiceImpl implements MyService {
    
        // Self autowired class to take advantage of proxied methods in the same class
        // See https://stackoverflow.com/questions/51922604/transactional-and-stream-in-spring/51923214#51923214
        private MyService myService;  
    
        private NotificationService notificationService;  
    
        @Override
        @Async
        public void doSomething(Long id) {
            // I can execute database operations here
            try {
                myService.doDatabaseOperations(...);
            } catch(DatabaseAccessException e) {
                AsyncErrorLog log = new AsyncErrorLog(ex);
                notificationService.saveLogAndNotify(log);
            }
            // Other exceptions (from DB operations or the notifications service) can be 
            // handled with other "catches" or to let the SimpleAsyncExHandler log them for you.
            // You can also use standard multithreading exception handling for this
        }
    
        @Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
        public void doDatabaseOperations(...) {
            ...
        }
    
    }
    
    这将帮助您:

    @Override
    public void createLog(Log log) {
    try {
        session = sessionFactory.getCurrentSession();
    } catch (HibernateException e) {
        session = sessionFactory.openSession();
    }
        session.saveOrUpdate(log);
    }
    

    您可以使用处理程序中的
    applicationContext
    查找
    notificationService
    。当我为处理程序使用
    @Autowired
    时,我也遇到了同样的问题,这反过来又注入了我的
    LogService
    。查看日志后,我看到
    TransactionSynchronizationManager
    在回滚异常后正在清除事务同步,除了
    no transaction for…
    错误之外,没有其他内容

    使用
    applicationContext
    查找
    logService
    bean并更改我的处理程序后,我在日志中看到了所需的结果

  • 开始
  • 初始化事务同步
  • 正在获取[..AsyncService.doAsync]的事务
  • 例外情况
  • 后退
  • 清算事务同步

  • 开始

  • 初始化事务同步
  • 获取[……LogService.save]的事务处理
  • 更改配置以包含界面
    ApplicationContextAware
    ,这将为您提供访问
    applicationContext
    的便捷方法。将其设置为实例变量

    请参阅下面的配置类

    @Configuration
    @EnableAsync
    public class AsyncConfig implements AsyncConfigurer, ApplicationContextAware {
    
        private static final int CORE_POOL_SIZE = 3;
        private static final int MAX_POOL_SIZE = 3;
        private static final int QUEUE_CAPACITY = 24;
        private static final String THREAD_NAME_PREFIX = "AsynchThread-";
    
        private ApplicationContext applicationContext;
    
        @Override
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(CORE_POOL_SIZE);
            executor.setMaxPoolSize(MAX_POOL_SIZE);
            executor.setQueueCapacity(QUEUE_CAPACITY);
            executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
            executor.initialize();
            return executor;
        }
    
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return new AsyncExceptionHandler(this.applicationContext);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    
    我已经从处理程序中删除了
    @组件
    ,并将其用作POJO。 每次调用带有异常的
    getAsyncUncaughtExceptionHandler
    时,都会创建一个新的处理程序实例,并将
    applicationContext
    作为依赖项

    public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    
        private final ApplicationContext applicationContext;
    
        public AsyncExceptionHandler(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }
    
        @Override
        public void handleUncaughtException(Throwable ex, Method method, Object... params) {
            Log log = new Log();
            log.setEntry(ex.getMessage());
            LogService logService = this.applicationContext.getBean(LogService.class);
            logService.save(log);
        }
    }
    
    logService
    上的
    save
    方法每次调用时都需要一个新事务

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save(Log log)
    

    请分享
    NotificationServiceImpl
    class@Spara:
    notificationserviceinpl
    调用
    LogDaoImpl
    获取错误的地方。我共享了该类的实现。您是否尝试将
    @Transactional
    添加到
    LogDaoImpl
    类的顶部?我们在
    NotificationServiceImpl
    的服务级别上有
    @Transactional
    ,除了
    AsyncUncaughtExceptionHandler
    。(更新了代码以显示服务实现)是的,我明白了,但错误意味着您的服务层无法成功启动事务,我试图在我的项目中说明您的情况,但我不能,可能是因为我使用了spring数据,请更改两件事并告诉我它是否有效:首先,请在
    AsyncExceptionHandler
    类中将
    @Component
    更改为
    @Service
    ,然后请在
    LogDaoImpl
    Hi@Dovmo添加
    @Transactional
    ,您能详细说明一下吗“如果您没有使用Spring数据,则必须确保通知服务在Hibernate会话上显式调用数据库调用。“我们没有使用Spring数据,如何确保通知服务在Hibernate会话上显式调用数据库调用?您的实现与我们现在解决问题的方式非常接近,但我们必须在每个@Async实现中重复相同的try/catch块。如果我们可以在一个地方处理它们,那就更好了。我设想了两种模块化方法:(1)使用AOP/拦截器注释或(2)可以使用
    doSomething
    @Async
    调用返回
    CompletableFuture
    。您可以让您的服务接受来自
    CompletableFuture
    #exc的异常