当step 3
操作失败并发生异常时,我想在数据库中创建异常日志。
您可以在下面看到@Async
和AsyncExecutorConfiguration
类的实现。
在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
类中进行有效的会话?
-
更新
这是AsyncExceptionHandler
和NotificationServiceImpl
的简化实现,在那里我们得到了错误。
LogDaoImpl.class
答案 0 :(得分:2)
每个Hibernate异常;如果您不使用Spring Data,则必须确保通知服务明确在Hibernate会话上调用数据库调用。
根据我的经验,UncaughtExceptionHandler
(通常)的主要用例用于:
RuntimeException
的简单的最后一种方法,程序员可能不知道这些Handler
出于某种原因不能(或没有)陷入代码中两者之间的共同点是此#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并更改处理程序之后,我在日志中看到了所需的结果。
清除事务同步
开始
更改配置以包括接口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)