在AsyncUncaughtExceptionHandler中获取Hibernate会话

时间:2018-12-14 19:13:25

标签: java spring hibernate session dao

step 3操作失败并发生异常时,我想在数据库中创建异常日志。

您可以在下面看到@AsyncAsyncExecutorConfiguration类的实现。

AsyncExceptionHandler类中,当我调用试图访问数据库的服务时,我得到:AsyncExceptionHandler

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread

@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类中进行有效的会话?

-

更新

这是AsyncExceptionHandlerNotificationServiceImpl的简化实现,在那里我们得到了错误。

LogDaoImpl.class

3 个答案:

答案 0 :(得分:2)

每个Hibernate异常;如果您不使用Spring Data,则必须确保通知服务明确在Hibernate会话上调用数据库调用。

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

  1. 用于处理RuntimeException的简单的最后一种方法,程序员可能不知道这些Handler出于某种原因不能(或没有)陷入代码中
  2. 一种捕获程序员无法控制的代码异常的方法(例如,如果您直接在某些第三方库上调用Async等)

两者之间的共同点是此#doSomething用于意外情况。实际上,Spring本身会解释您自己的代码和Spring Async {em>已经 sets a default one for you that will log to the console(代码here)中的“意外情况”,从而使您不必担心流氓异常被杀死线程,不知道为什么。 (注意:源代码中的消息表示它正在捕获“意外”异常。当然,异常是意外的,但是这些是您确实不知道会发生的异常。SpringAsync将记录该异常为你。)

在您的示例中就是这种情况,因为您正在执行Spring数据库操作,并且应该确切了解try-catch内部正在发生什么,所以我只需要删除AUEH a -finally(和/或#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(...) { ... } } 内部处理异常:

print(tree1[2][1])
print(tree2[0][2][1])

答案 1 :(得分:1)

这将帮助您:

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

答案 2 :(得分:1)

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

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

  1. 开始
  2. 初始化事务同步
  3. 获取[.... AsyncService.doAsync]的交易
  4. 例外
  5. 回滚
  6. 清除事务同步

  7. 开始

  8. 初始化事务同步
  9. 获取[..... 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;
    }
}

我从处理程序中删除了@Component并将其用作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);
    }
}

save上的logService方法每次调用都需要一个新事务。

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