Spring事务分析:@Transactional用久了,是不是忘了编程式事务了?


一、编程式事务和声明式事务的区别

1. 编程式事务

  • 定义 : 通过手动编码控制事务的开启、提交和回滚,通常使用 TransactionTemplatePlatformTransactionManager

  • 特点: 灵活性高,但代码侵入性强。

  • 代码实践 :

    java 复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.support.TransactionCallback;
    import org.springframework.transaction.support.TransactionTemplate;
    
    @Service
    public class ProgrammaticTransactionService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        @Autowired
        private TransactionTemplate transactionTemplate;
    
        public void updateDataWithTransaction() {
            transactionTemplate.execute(new TransactionCallback<Void>() {
                @Override
                public Void doInTransaction(TransactionStatus status) {
                    try {
                        // 业务逻辑1: 插入数据
                        jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Alice", 25);
                        // 模拟异常
                        if (true) {
                            throw new RuntimeException("Simulated error");
                        }
                        // 业务逻辑2: 更新数据
                        jdbcTemplate.update("UPDATE user SET age = ? WHERE name = ?", 26, "Alice");
                        return null;
                    } catch (Exception e) {
                        status.setRollbackOnly(); // 手动标记回滚
                        throw e;
                    }
                }
            });
        }
    }
    • 说明 : 如果发生异常,status.setRollbackOnly() 标记事务回滚,插入操作会被撤销。

2. 声明式事务

  • 定义 : 使用 @Transactional 注解,通过 Spring AOP 自动管理事务。

  • 特点: 代码简洁,但受限于 AOP 机制。

  • 代码实践 :

    java 复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    public class DeclarativeTransactionService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Transactional
        public void updateDataWithTransaction() {
            // 业务逻辑1: 插入数据
            jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Bob", 30);
            // 模拟异常
            if (true) {
                throw new RuntimeException("Simulated error");
            }
            // 业务逻辑2: 更新数据
            jdbcTemplate.update("UPDATE user SET age = ? WHERE name = ?", 31, "Bob");
        }
    }
    • 说明 : 默认情况下,抛出 RuntimeException 会自动触发回滚,插入操作被撤销。

区别总结

特性 编程式事务 声明式事务
实现方式 TransactionTemplate 或手动调用 @Transactional 注解
代码示例 显式控制事务边界 注解驱动,自动管理
灵活性 高,可动态调整逻辑 较低,依赖配置
侵入性 高,事务代码嵌入业务逻辑 低,与业务逻辑分离

二、@Transactional 的默认行为和局限性

1. 默认行为

  • 异常捕获 : 只对 RuntimeExceptionError 触发回滚。

  • 代码实践 :

    java 复制代码
    @Service
    public class DefaultBehaviorService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Transactional
        public void defaultRollback() throws Exception {
            jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Charlie", 35);
            // 抛出非 RuntimeException,不会回滚
            throw new Exception("Not a RuntimeException");
        }
    
        @Transactional(rollbackFor = Exception.class)
        public void explicitRollback() throws Exception {
            jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "David", 40);
            // 显式指定 rollbackFor,回滚生效
            throw new Exception("Explicit rollback");
        }
    }
    • 结果 :
      • defaultRollback: 事务不回滚,数据插入成功。
      • explicitRollback: 事务回滚,数据插入被撤销。

2. 局限性及代码实践

  • 方法可见性 : 仅对 public 方法生效。

    java 复制代码
    @Service
    public class VisibilityService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Transactional
        private void privateMethod() {
            jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Eve", 45);
            throw new RuntimeException("Test rollback");
        }
    
        public void callPrivateMethod() {
            privateMethod(); // 直接调用,事务不生效
        }
    }
    • 结果 : privateMethod@Transactional 被忽略,数据插入成功。
  • 自调用: 同一个类内方法调用,事务失效。

    java 复制代码
    @Service
    public class SelfInvocationService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void methodA() {
            methodB(); // 自调用,事务不生效
        }
    
        @Transactional
        public void methodB() {
            jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Frank", 50);
            throw new RuntimeException("Test rollback");
        }
    }
    • 结果 : methodB@Transactional 不生效,数据插入成功。
    • 解决办法: 使用自注入。
    java 复制代码
    @Service
    public class SelfInvocationFixService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        @Autowired
        private SelfInvocationFixService self;
    
        public void methodA() {
            self.methodB(); // 通过代理调用,事务生效
        }
    
        @Transactional
        public void methodB() {
            jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Grace", 55);
            throw new RuntimeException("Test rollback");
        }
    }
    • 结果: 事务回滚,数据插入被撤销。
  • 代理对象: 非代理对象调用,事务失效。

    java 复制代码
    public class ProxyIssueExample {
        public static void main(String[] args) {
            DeclarativeTransactionService service = new DeclarativeTransactionService();
            service.updateDataWithTransaction(); // 直接 new,事务不生效
        }
    }
    • 结果: 事务不生效,需通过 Spring 容器获取代理对象。

三、TransactionManagerTransactionAspectSupport 结合触发回滚

1. TransactionManager

  • 代码实践 (编程式事务直接使用 TransactionManager):

    java 复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.TransactionDefinition;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.support.DefaultTransactionDefinition;
    
    @Service
    public class TransactionManagerService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        @Autowired
        private PlatformTransactionManager transactionManager;
    
        public void manualTransaction() {
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            TransactionStatus status = transactionManager.getTransaction(def);
            try {
                jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Helen", 60);
                throw new RuntimeException("Test rollback");
            } catch (Exception e) {
                transactionManager.rollback(status); // 手动回滚
                throw e;
            }
            // 如果没有异常,手动提交
            // transactionManager.commit(status);
        }
    }
    • 结果: 事务回滚,数据插入被撤销。

2. TransactionAspectSupport

  • 代码实践 (结合 @Transactional):

    java 复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    import org.springframework.transaction.interceptor.TransactionAspectSupport;
    
    @Service
    public class TransactionAspectService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Transactional
        public void forceRollback() {
            jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?, ?)", "Ivy", 65);
            // 手动触发回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
    • 结果: 事务回滚,数据插入被撤销。
    • 说明 : @Transactional 通过 TransactionInterceptor(继承自 TransactionAspectSupport)调用 TransactionManager 管理事务,异常或手动标记回滚时触发 rollback()

3. Spring Boot 配置

  • 自动配置示例 :

    java 复制代码
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import javax.sql.DataSource;
    
    @Configuration
    public class TransactionConfig {
        @Bean
        public DataSourceTransactionManager transactionManager(DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    }
    • Spring Boot 默认自动配置 TransactionManager,无需手动定义,除非需要自定义。

相关推荐
失业写写八股文25 分钟前
Spring基础:Spring的事物哪些情况下会失效
java·后端·spring
失业写写八股文2 小时前
Redis中keys命令的缺点
redis·后端
问道飞鱼3 小时前
【Springboot知识】开发属于自己的中间件健康监测HealthIndicate
spring boot·后端·中间件·healthindicate
luckyext5 小时前
Postman用JSON格式数据发送POST请求及注意事项
java·前端·后端·测试工具·c#·json·postman
程序视点5 小时前
Redis集群机制及一个Redis架构演进实例
java·redis·后端
鱼樱前端5 小时前
Navicat17基础使用
java·后端
黑风风5 小时前
深入理解Spring Boot Starter及如何自定义Starter
java·spring boot·后端
uhakadotcom5 小时前
BM25 算法入门与实践
后端
鱼樱前端5 小时前
Mac M1安装MySQL步骤
java·后端
uhakadotcom6 小时前
Istio 服务网格:连接、保护和优化微服务的利器
后端·面试·github