[英]Getting Hibernate Session inside AsyncUncaughtExceptionHandler
I want to create an exception log in the database when an @Async
operation fails with an exception. 当
@Async
操作因异常而失败时,我想在数据库中创建异常日志。
You can see the implementation for AsyncExecutorConfiguration
and AsyncExceptionHandler
classes below. 您可以在下面看到
AsyncExecutorConfiguration
和AsyncExceptionHandler
类的实现。
Inside AsyncExceptionHandler
class, when I call a service that tries to access the database, I am getting: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
在
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
function itself already has a valid session. @Async
函数本身已经有一个有效的会话。 What should I do to have a valid session in AsyncExceptionHandler
class too? 我该怎么做才能在
AsyncExceptionHandler
类中有一个有效的会话?
-- -
UPDATE UPDATE
Here is the simplified implementations for NotificationServiceImpl
and LogDaoImpl.class
where we get the error. 以下是
NotificationServiceImpl
和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);
}
Per the Hibernate exception; 根据Hibernate异常; if you're not using Spring Data, you'll have to make sure the notification service explicitly invokes the database calls on the Hibernate session.
如果您不使用Spring Data,则必须确保通知服务明确调用Hibernate会话上的数据库调用。
On another note, in my experience, the main use cases for the UncaughtExceptionHandler
(in general) are used for: 另一方面,根据我的经验,
UncaughtExceptionHandler
(通常)的主要用例用于:
RuntimeException
s that may be unknown to the programmer that for some reason cannot (or are not) caught in code RuntimeException
的简单的最后手段,程序员可能不知道由于某种原因不能(或没有)捕获代码 The commonality between the two is that this Handler
is used for something unexpected. 两者之间的共性是这个
Handler
用于意外的事情。 In fact, Spring itself accounts for the "unexpectedness" in your own code and Spring Async already sets a default one for you that will log to the console (code here ), letting you not have to worry about rogue exceptions killing threads and not knowing why. 事实上,Spring本身在你自己的代码中占据了“意外”,Spring Async 已经 为你设置了一个默认的登录控制台 ( 这里的代码),让你不必担心流氓异常会杀死线程而不知道为什么。 (Note: The message in the source code says it's catching an "unexpected" exception. Of course exceptions are unexpected, but these are one's that you really didn't know could happen. Spring Async will log it for you.)
(注意:源代码中的消息表明它正在捕获“意外”异常。当然异常是意外的,但这些是您真正不知道的可能发生的事情.Spring Async会为您记录它。)
That being the case, in your example, since you're doing Spring Database operations and should know exactly what's happening inside of #doSomething
, I would just go with removing the AUEH a try-catch
(and/or -finally
) and handle the exception inside of #doSomething
: 既然如此,在您的示例中,由于您正在进行Spring数据库操作并且应该确切地知道
#doSomething
内部发生了#doSomething
,我只需要将AUEH移除一个try-catch
(和/或-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(...) {
...
}
}
This will help you: 这将有助于您:
@Override
public void createLog(Log log) {
try {
session = sessionFactory.getCurrentSession();
} catch (HibernateException e) {
session = sessionFactory.openSession();
}
session.saveOrUpdate(log);
}
You can use the applicationContext
in your handler to lookup the notificationService
. 您可以在处理
applicationContext
中使用applicationContext
来查找notificationService
。 I had the same issue when I used @Autowired
for the handler, which in turn injected my LogService
. 当我使用
@Autowired
处理程序时,我遇到了同样的问题,后者又注入了我的LogService
。 After looking at the logs I saw that the TransactionSynchronizationManager
is clearing transaction synchronization after the rollback of the exception and nothing else except the no transaction for ......
error. 在查看日志之后,我看到
TransactionSynchronizationManager
在回滚异常后清除了事务同步,除了no transaction for ......
错误的no transaction for ......
之外没有别的no transaction for ......
错误。
After using the applicationContext
for looking up the logService
bean and changing my handler, I saw the desired result in the logs. 在使用
applicationContext
查找logService
bean并更改我的处理程序之后,我在日志中看到了所需的结果。
Clearing transaction synchronization 清除事务同步
begin 开始
Change your config to include the interface ApplicationContextAware
which will give you a convenience method to access the applicationContext
. 更改您的配置以包含
ApplicationContextAware
接口,它将为您提供访问applicationContext
的便捷方法。 Set it as a instance variable. 将其设置为实例变量。
See my configuration class below. 请参阅下面的配置类。
@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;
}
}
I have removed the @Component
from the handler and use it as a POJO. 我已从处理程序中删除
@Component
并将其用作POJO。 Every time getAsyncUncaughtExceptionHandler
is called with an exception, a new handler instance is created with the applicationContext
as a dependency. 每次使用异常调用
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);
}
}
The save
method on logService
requires a new transaction every time it is called. logService
上的save
方法每次调用时都需要一个新事务。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(Log log)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.