You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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实例,该方案存在哪些弊端或需要注意的问题?


主要弊端

  1. 资源开销激增:每次创建EntityManager都会初始化会话、一级缓存、事务上下文等资源,高频调用会导致大量对象的创建与销毁,显著增加GC压力,降低系统整体吞吐量。
  2. 事务管理风险高:手动控制事务的begin/commit/rollback,一旦遗漏异常处理(比如未捕获的RuntimeException),会导致事务悬挂、数据不一致甚至数据库连接泄漏。
  3. 丧失Spring事务优势:无法利用Spring声明式事务的便捷性、事务传播机制,也无法与Spring的事务同步资源(比如JMS消息、缓存)集成。
  4. 缓存复用失效:每个EntityManager拥有独立的一级缓存,多次查询同一实体时无法复用缓存结果,会额外增加数据库查询次数,提升数据库负载。
  5. 连接池压力增大:每次EntityManager创建都会从连接池获取数据库连接,高频场景下可能导致连接池耗尽,引发连接等待超时。

关键注意事项

  1. 强制资源释放: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);
    }
}
  1. 异常处理必须严谨:所有数据库操作都要包裹在try-catch块中,确保任何异常场景下事务都能正确回滚,避免脏数据写入。
  2. 考虑线程内复用EntityManager:可以通过ThreadLocal在同一异步线程内复用EntityManager,减少重复创建开销,但要注意在任务结束时清理ThreadLocal中的资源,避免内存泄漏。
  3. 监控资源指标:高频场景下需监控数据库连接池的活跃连接数、GC频率、响应时间等指标,及时调整连接池参数或优化代码。
  4. 优先排查WebSphere事务失效根源:手动方案只是临时 workaround,建议从以下方向排查根本原因:
    • 检查WebSphere的JTA事务配置是否与Spring事务管理器冲突
    • 确认@EnableAsync和@EnableTransactionManagement注解是否正确配置,且代理模式适配WebSphere环境
    • 验证Spring AOP是否在WebSphere中正常生效(比如目标类是否被final修饰导致无法生成代理)
    • 确认EntityManagerFactory是否正确绑定到WebSphere的全局事务管理器

内容的提问来源于stack exchange,提问作者Mahmoud Saleh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:27:33