1. 引言

本文将深入讲解 Java 中的事务机制,涵盖本地事务(resource local transaction)与全局事务(global transaction)的实现方式。同时,我们会探讨在纯 Java 和 Spring 框架中如何高效管理事务,帮助你在实际项目中避免踩坑。

对于有经验的开发者来说,事务不是陌生概念,但跨资源事务的复杂性和性能影响往往被低估。本文将从底层机制到高层抽象,带你系统掌握 Java 事务的核心要点。


2. 什么是事务?

事务本质上是一组必须全部成功,否则全部回滚的操作集合。✅
如果其中任何一个操作失败,整个操作序列必须回退(rollback),确保应用状态不被破坏。

⚠️ 事务的核心目标:保证数据一致性(ACID)

在 Java 中,事务可能涉及多种资源:

  • 数据库(如 MySQL)
  • 消息队列(如 ActiveMQ)
  • 分布式缓存等

当事务只涉及单一资源时,称为 本地事务
当事务跨越多个资源(如数据库 + 消息队列),则称为 全局事务分布式事务


3. 本地事务

本地事务指在单个资源内部执行的一组操作,作为一个不可分割的单元。
常见的资源包括数据库、消息队列等。下面分别介绍 JDBC、JPA、JMS 中的本地事务使用方式。

3.1. JDBC 事务

JDBC 是 Java 访问数据库的标准 API。每个 Connection 默认处于 auto-commit 模式,即每条 SQL 语句自动提交。

要实现多条 SQL 作为一个事务执行,必须手动关闭 auto-commit,并显式控制提交或回滚:

Connection connection = DriverManager.getConnection(CONNECTION_URL, "user", "password");
try {
    connection.setAutoCommit(false); // 关闭自动提交
    PreparedStatement firstStatement = connection.prepareStatement("INSERT INTO users (name) VALUES ('Alice')");
    firstStatement.executeUpdate();
    PreparedStatement secondStatement = connection.prepareStatement("INSERT INTO logs (action) VALUES ('user_created')");
    secondStatement.executeUpdate();
    connection.commit(); // 手动提交
} catch (Exception e) {
    connection.rollback(); // 出错回滚
} finally {
    connection.close();
}

✅ 要点总结:

  • 使用 setAutoCommit(false) 开启事务
  • commit() 提交,rollback() 回滚
  • 可通过 Savepoint 实现部分回滚

3.2. JPA 事务

JPA 是 Java 中用于对象关系映射(ORM)的规范,常见实现有 Hibernate、EclipseLink 等。

JPA 通过 EntityManager 管理实体对象,其操作在 持久化上下文(persistence context) 中进行。默认情况下,该上下文是事务范围的(transaction-scoped)。

手动管理 JPA 事务示例:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-example");
EntityManager em = emf.createEntityManager();
try {
    em.getTransaction().begin();
    em.persist(new User("Bob"));
    em.persist(new Log("user_created"));
    em.getTransaction().commit();
} catch (Exception e) {
    em.getTransaction().rollback();
} finally {
    em.close();
    emf.close();
}

⚠️ 注意:

  • EntityManager 需配合 Transaction 使用
  • 在 Spring 中通常由容器自动管理,无需手动调用

3.3. JMS 事务

JMS 是 Java 的消息服务规范,支持 ActiveMQ、IBM MQ 等消息中间件。

JMS 支持事务性会话(transacted session),允许将多个消息发送操作打包为一个事务。

ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
try {
    Session session = connection.createSession(true, 0); // 第一个参数 true 表示事务会话
    Destination destination = session.createTopic("ORDER_EVENTS");
    MessageProducer producer = session.createProducer(destination);
    producer.send(session.createTextMessage("OrderCreated"));
    producer.send(session.createTextMessage("PaymentProcessed"));
    session.commit(); // 提交事务
} catch (Exception e) {
    session.rollback(); // 回滚
} finally {
    connection.close();
}

✅ 关键点:

  • 创建 Session 时传入 true 启用事务
  • 生产和消费不能在同一个事务中(消息中间件限制)
  • 事务范围:客户端 ↔ 消息服务器

JMS Architecture


4. 全局事务(分布式事务)

当业务逻辑涉及多个资源(如数据库 + 消息队列),本地事务无法保证整体一致性。此时需要 全局事务 来协调多个资源。

典型的场景:

  • 向数据库插入订单
  • 向消息队列发送“订单创建”事件

这两个操作必须同时成功或同时失败,否则数据将不一致。

4.1. JTA(Java Transaction API)

JTA 是 Java EE 提供的分布式事务标准 API,基于 XA 协议实现,支持两阶段提交(2PC)。

核心接口:

  • TransactionManager:由应用服务器使用,控制事务生命周期
  • UserTransaction:供应用代码显式控制事务边界
  • XAResource:资源管理器(如数据库、MQ)与事务管理器的桥梁

JTA 抽象了底层资源的差异,让多个 XA 兼容资源能参与同一个全局事务。

4.2. JTS(Java Transaction Service)

JTS 是 JTA 的底层实现规范,基于 CORBA OTS 标准,负责在分布式环境中传播事务上下文。

⚠️ 实际开发中,JTS 通常由应用服务器(如 WebLogic、WebSphere)内置提供,开发者无需直接操作。


5. JTA 事务管理

5.1. 应用服务器中的 JTA

在传统 Java EE 应用服务器中,JTA 依赖容器提供事务管理服务。

两种事务管理方式:

  • 容器管理事务(CMT):由 EJB 容器自动控制事务边界,代码简洁但灵活性差
  • Bean 管理事务(BMT):开发者通过 UserTransaction 手动控制,灵活但复杂

⚠️ 缺点:

  • 应用强依赖应用服务器
  • 难以测试、部署和迁移
  • 不适合微服务架构

5.2. 独立部署的 JTA(Standalone JTA)

为解耦应用服务器,可使用独立事务管理器,如 AtomikosBitronix

以下示例使用 Atomikos 实现数据库 + 消息队列的分布式事务:

配置 XA 数据源

AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
atomikosDataSourceBean.setUniqueResourceName("xads");
// 设置数据库连接信息
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
mysqlXaDataSource.setUser("user");
mysqlXaDataSource.setPassword("password");
atomikosDataSourceBean.setXaDataSource(mysqlXaDataSource);
DataSource dataSource = atomikosDataSourceBean;

配置 XA 消息连接工厂

AtomikosConnectionFactoryBean atomikosConnectionFactoryBean = new AtomikosConnectionFactoryBean();
atomikosConnectionFactoryBean.setXaConnectionFactory(new ActiveMQXAConnectionFactory("tcp://localhost:61616"));
atomikosConnectionFactoryBean.setUniqueResourceName("xamq");
ConnectionFactory connectionFactory = atomikosConnectionFactoryBean;

使用 UserTransaction 控制事务

UserTransaction userTransaction = new UserTransactionImp();
userTransaction.setTransactionTimeout(300);

try {
    userTransaction.begin();

    // 1. 数据库操作
    java.sql.Connection dbConn = dataSource.getConnection();
    PreparedStatement ps = dbConn.prepareStatement("INSERT INTO orders (item) VALUES (?)");
    ps.setString(1, "laptop");
    ps.executeUpdate();

    // 2. 消息发送
    javax.jms.Connection mqConn = connectionFactory.createConnection();
    Session session = mqConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
    MessageProducer producer = session.createProducer(session.createQueue("ORDER_QUEUE"));
    producer.send(session.createTextMessage("New order: laptop"));

    userTransaction.commit(); // 两阶段提交
} catch (Exception e) {
    userTransaction.rollback(); // 全部回滚
}

✅ Atomikos 的优势:

  • 无需应用服务器
  • 自动注册 XA 资源
  • 易于集成到 Spring

6. Spring 中的事务支持

Spring 提供了统一的事务抽象层,屏蔽了 JDBC、JPA、JMS、JTA 等不同资源的差异,极大简化了事务管理。

其核心机制是 AOP 代理 + TransactionManager

Spring Transactions

6.1. Spring 配置示例(集成 Atomikos)

配置 JTA 事务管理器

@Bean
public PlatformTransactionManager platformTransactionManager() throws Throwable {
    JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
    jtaTransactionManager.setTransactionManager(transactionManager());
    jtaTransactionManager.setUserTransaction(userTransaction());
    return jtaTransactionManager;
}

@Bean
public UserTransaction userTransaction() {
    UserTransactionImp ut = new UserTransactionImp();
    ut.setTransactionTimeout(300);
    return ut;
}

@Bean(initMethod = "init", destroyMethod = "close")
public TransactionManager transactionManager() {
    UserTransactionManager utm = new UserTransactionManager();
    utm.setForceShutdown(false);
    return utm;
}

配置 JMS 和 JPA

@Bean
public JmsTemplate jmsTemplate() throws Throwable {
    return new JmsTemplate(connectionFactory());
}

@Bean
public ConnectionFactory connectionFactory() {
    ActiveMQXAConnectionFactory xaConnectionFactory = new ActiveMQXAConnectionFactory("tcp://localhost:61616");
    AtomikosConnectionFactoryBean cfBean = new AtomikosConnectionFactoryBean();
    cfBean.setXaConnectionFactory(xaConnectionFactory);
    cfBean.setUniqueResourceName("xamq");
    cfBean.setLocalTransactionMode(false);
    return cfBean;
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    Properties jpaProperties = new Properties();
    jpaProperties.setProperty("javax.persistence.transactionType", "JTA");
    em.setJpaProperties(jpaProperties);
    return em;
}

@Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource() throws SQLException {
    MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
    mysqlXaDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
    mysqlXaDataSource.setUser("user");
    mysqlXaDataSource.setPassword("password");
    AtomikosDataSourceBean dsBean = new AtomikosDataSourceBean();
    dsBean.setXaDataSource(mysqlXaDataSource);
    dsBean.setUniqueResourceName("xads");
    return dsBean;
}

6.2. 事务管理方式

✅ 声明式事务(推荐)

使用 @Transactional 注解,简单粗暴:

@PersistenceContext
private EntityManager entityManager;

@Autowired
private JmsTemplate jmsTemplate;

@Transactional(propagation = Propagation.REQUIRED)
public void processOrder(Order order) {
    entityManager.persist(order);
    jmsTemplate.convertAndSend("ORDER_QUEUE", order);
}

Spring 会在方法执行前后自动开启/提交事务,异常时回滚。

✅ 编程式事务(精细控制)

适用于复杂逻辑,需手动控制事务边界:

@Autowired
private PlatformTransactionManager transactionManager;

public void processOrder(Order order) {
    TransactionTemplate txTemplate = new TransactionTemplate(transactionManager);
    txTemplate.executeWithoutResult(status -> {
        entityManager.persist(order);
        jmsTemplate.convertAndSend("ORDER_QUEUE", order);
    });
}

7. 总结与最佳实践

分布式事务虽然强大,但代价高昂。以下是关键建议:

优先使用独立事务管理器(如 Atomikos)
避免绑定应用服务器,提升可移植性,更适合微服务。

善用 Spring 的事务抽象
屏蔽底层差异,业务代码无需关心 JDBC、JPA 或 JTA 细节。

谨慎设计事务边界
事务是阻塞的,影响性能。应尽量缩短事务范围,避免长事务。

能不用分布式事务就不用
考虑使用 最终一致性 + 补偿机制(Saga 模式) 替代强一致性事务,提升系统吞吐量。

❌ 踩坑提醒:不要在 @Transactional 方法中调用本类的其他方法,会导致 AOP 代理失效!


8. 结论

本文系统讲解了 Java 中的事务机制:

  • 本地事务(JDBC、JPA、JMS)
  • 全局事务(JTA/XA)
  • 独立事务管理(Atomikos)
  • Spring 的声明式与编程式事务

Spring + Atomikos 的组合,既保留了分布式事务的一致性保障,又解耦了应用服务器依赖,是现代 Java 应用的推荐方案。

但请记住:事务不是银弹,能不用就不用,能小就不要大


原始标题:Introduction to Transactions in Java and Spring