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
(通常)的主要用例用于:
运行时异常
的一种简单的最后手段,程序员可能不知道这些异常由于某种原因无法(或没有)在代码中捕获处理程序
用于一些意想不到的事情。事实上,Spring本身解释了您自己的代码和Spring Async(代码)中的“意外”,让您不必担心流氓异常会杀死线程并且不知道原因。(注意:源代码中的消息说它正在捕获一个“意外”异常。当然,异常是意外的,但这些异常是您真正不知道可能发生的。Spring Async将为您记录。)
在您的示例中,就是这样,因为您正在执行Spring数据库操作,并且应该确切地知道#doSomething
内部发生了什么,所以我只需要删除AUEH atry 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的异常