Websphere中@Transactional与@Async配合失效及方案考量
问题:@Async方法结合@Transactional在WebSphere中失效后的手动EntityManager方案弊端分析
场景与初始实现
需求是在@Async异步方法中通过@Transactional跟踪文件转换流程,初始代码如下:
@Async @Transactional public void convertFile(String documentId) { CustomLog customLog = new CustomLog(); customLog.setStatus("IN_PROGRESS"); mySpringDataRepo.save(customLog); try { doConvertFunction(documentId); } catch (Exception e) { customLog.setStatus("FAIL"); mySpringDataRepo.save(customLog); return; } customLog.setStatus("SUCCESS"); mySpringDataRepo.save(customLog); }
使用技术栈:
- Spring Boot 2.7.18
- Hibernate Core 5.6.15.Final
- Websphere ND 9.0.5.13
- Java 1.8
自定义EntityManagerFactory配置:
@Primary @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource) { LocalContainerEntityManagerFactoryBean em = builder.dataSource(dataSource).packages("com.myapp").persistenceUnit("MyEntityManagerFactory").properties(jpaProperties()).build(); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); return em; } protected Map<String, Object> jpaProperties() { Map<String, Object> props = new HashMap<>(); props.put("hibernate.physical_naming_strategy", CamelCaseToUnderscoresNamingStrategy.class.getName()); props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName()); return props; }
问题现象
上述代码在Tomcat 9中运行正常,但在WebSphere环境中事务无法开启,save方法完全不执行。
尝试过以下无效方案:
- 使用
@Transactional(propagation = Propagation.REQUIRES_NEW) - 将save逻辑提取到独立服务类:
@Service public class MyService { @Autowired private MySpringDataRepo mySpringDataRepo; @Transactional(propagation = Propagation.REQUIRES_NEW) public CustomLog save(CustomLog customLog) { mySpringDataRepo.save(customLog); return customLog; } }
临时有效方案:手动创建EntityManager
唯一在WebSphere中生效的方式是通过EntityManagerFactory手动创建EntityManager并管理事务,实现如下:
@Service public class CustomLogService { @Autowired private EntityManagerFactory entityManagerFactory; public Long save(CustomLog customLog) { EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); entityManager.persist(customLog); entityManager.flush(); entityManager.getTransaction().commit(); return customLog.getId(); } public CustomLog find(Long id) { EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); CustomLog customLog = entityManager.find(CustomLog.class, id); entityManager.getTransaction().commit(); return customLog; } public void update(CustomLog customLog) { EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); entityManager.merge(customLog); entityManager.getTransaction().commit(); } }
修改后的@Async方法:
@Async public void convertFile(String documentId) { CustomLog customLog = new CustomLog(); customLog.setStatus("IN_PROGRESS"); customLogService.save(customLog); try { doConvertFunction(documentId); } catch (Exception e) { customLog = customLogService.find(customLog.getId()); customLog.setStatus("FAIL"); customLogService.update(customLog); return; } customLog = customLogService.find(customLog.getId()); customLog.setStatus("SUCCESS"); customLogService.update(customLog); }
注:尝试通过@PersistenceContext(name = "MyEntityManagerFactory")注入EntityManager无效,仅手动创建可行。
核心疑问:高频使用下该方案的弊端与注意事项
由于文件转换是高频操作,每次调用都会生成新的EntityManager实例,该方案存在哪些弊端或需要注意的问题?
主要弊端
- 资源开销激增:每次创建EntityManager都会初始化会话、一级缓存、事务上下文等资源,高频调用会导致大量对象的创建与销毁,显著增加GC压力,降低系统整体吞吐量。
- 事务管理风险高:手动控制事务的
begin/commit/rollback,一旦遗漏异常处理(比如未捕获的RuntimeException),会导致事务悬挂、数据不一致甚至数据库连接泄漏。 - 丧失Spring事务优势:无法利用Spring声明式事务的便捷性、事务传播机制,也无法与Spring的事务同步资源(比如JMS消息、缓存)集成。
- 缓存复用失效:每个EntityManager拥有独立的一级缓存,多次查询同一实体时无法复用缓存结果,会额外增加数据库查询次数,提升数据库负载。
- 连接池压力增大:每次EntityManager创建都会从连接池获取数据库连接,高频场景下可能导致连接池耗尽,引发连接等待超时。
关键注意事项
- 强制资源释放:EntityManager是资源密集型对象,使用后必须调用
close()方法释放,否则会导致数据库连接泄漏。建议使用try-with-resources语法自动管理:
public Long save(CustomLog customLog) { try (EntityManager entityManager = entityManagerFactory.createEntityManager()) { entityManager.getTransaction().begin(); entityManager.persist(customLog); entityManager.flush(); entityManager.getTransaction().commit(); return customLog.getId(); } catch (Exception e) { // 确保异常时回滚事务 if (entityManager != null && entityManager.getTransaction().isActive()) { entityManager.getTransaction().rollback(); } throw new RuntimeException("保存日志失败", e); } }
- 异常处理必须严谨:所有数据库操作都要包裹在try-catch块中,确保任何异常场景下事务都能正确回滚,避免脏数据写入。
- 考虑线程内复用EntityManager:可以通过ThreadLocal在同一异步线程内复用EntityManager,减少重复创建开销,但要注意在任务结束时清理ThreadLocal中的资源,避免内存泄漏。
- 监控资源指标:高频场景下需监控数据库连接池的活跃连接数、GC频率、响应时间等指标,及时调整连接池参数或优化代码。
- 优先排查WebSphere事务失效根源:手动方案只是临时 workaround,建议从以下方向排查根本原因:
- 检查WebSphere的JTA事务配置是否与Spring事务管理器冲突
- 确认
@EnableAsync和@EnableTransactionManagement注解是否正确配置,且代理模式适配WebSphere环境 - 验证Spring AOP是否在WebSphere中正常生效(比如目标类是否被final修饰导致无法生成代理)
- 确认EntityManagerFactory是否正确绑定到WebSphere的全局事务管理器
内容的提问来源于stack exchange,提问作者Mahmoud Saleh
相关产品推荐
相关产品推荐

