@Async is killing Hibernate transaction
Hibernate transactions work on a ThreadLocal
basis.
As you use another thread with @Async
there will be no transaction active.
You can achieve this functionality by having the async method calling another bean which is annotated by @Transactional
.
Here I explain this approach a little bit more: How do I properly do a background thread when using Spring Data and Hibernate?
Dejell
I like traveling around the world. So far I have been to: USA England Italy Slovania Croatia Jordan South Africa Zimbabwe Botswana France Canada Israel Thailand Switzerland Holland Bulgaria I am going to Vietnam soon
Updated on June 04, 2022Comments
-
Dejell almost 2 years
I am using an Open-Session-In-View transaction model for my REST api like this:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) sessionFactory.getCurrentSession().beginTransaction(); chain.doFilter(request, response); sessionFactory.getCurrentSession().getTransaction().commit(); }
This work just fine. I wanted to add @Async abilities. So I created:
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override @Bean(destroyMethod="shutdown") public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(18); executor.setMaxPoolSize(18); executor.initialize(); executor.setDaemon(true); executor.setWaitForTasksToCompleteOnShutdown(false); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new SimpleAsyncUncaughtExceptionHandler(); } }
and:
@Component public class AsyncMarketCaller { @Inject MarketManagerFactory marketManagerFactory; @Async public Future<List<Product>> getProducts(){ MarketManager productManager = marketManagerFactory.obtainMarketManager(market); result = productManager.getProducts(); .... } }
The productManager makes a call to another @Service
@Service public class DefaultIdentifierManager implements IdentifierManager{ @Inject UpcEanDAO upcEanDAO; @Override public String getTitleForIdentifier(String identifier){ UpcEan upcEan = upcEanDAO.find(identifier); } }
however, for
upcEanDAO.find(identifier)
I get an exception:Caused by: org.hibernate.HibernateException: get is not valid without active transaction
Before I added the
@Async
ability to make async calls togetProducts()
it worked just fine so I assume that@Async
kills the transaction that I opened with Hibernate.I tried adding based on another answer here,
@Transactional
to the method annotated with@Async
but it doesn't help.Any idea?
EDITED
I edited the code so
@Component public class AsyncMarketCaller { @Inject AsyncMarketService asyncMarketService; @Async public Future<List<Product>> getProducts(){ asyncMarketService.getProducts(); } }
and
@Service public class AsyncMarketService { @Inject MarketManagerFactory marketManagerFactory; @Transactional public Future<List<Product>> getProducts() .... } }
I see in the log
50689 DEBUG [localhost-startStop-1] org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'AsyncMarketService.getProducts' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
but it doesn't help. Please note that my method AsyncMarketService.getProducts doesn't call directly the DB, it calls to other methods and only one of them will make the call.
I also added above the one that actually make the call to DB:@Transactional
49992 DEBUG [localhost-startStop-1] org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'DefaultIdentifierManager.getTitleForIdentifier' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '
'