天天看点

神器TransactionTemplate,让事务管理更加便捷与安全!

作者:半熵
神器TransactionTemplate,让事务管理更加便捷与安全!

一、事务基本概念

事务是数据库系统中的重要概念,它是由一组操作所组成的逻辑单位,这组操作被视为一个整体,要么全部执行成功,要么全部回滚。事务的隔离级别可以控制多个事务之间的相互影响,而事务的传播行为可以控制当前事务对其他事务的可见性。

二、Spring 中的事务管理

Spring 提供了两种事务管理方式:编程式事务管理和声明式事务管理。编程式事务管理需要开发人员自己编写事务管理代码。而声明式事务管理则由 Spring 容器负责管理,需要开发人员在配置文件或者代码中指定事务管理规则。

声明式事务管理又分为两种实现方式:基于 TransactionProxyFactoryBean 的声明式事务管理和基于 @Transactional 注解的声明式事务管理。前者需要手动创建代理对象,后者则使用注解方式简化了配置。

编程式事务管理 声明式事务管理
优点 可以更加灵活地控制事务 代码简洁,易于维护
可以适应复杂的业务场景 使用 AOP 技术,与业务逻辑解耦
可以在代码中针对不同的异常进行回滚或提交 配置方便,可以通过配置文件轻松地进行切换和修改
缺点 代码量较大 对于高度定制化的业务场景,可能会出现限制
不易维护 对于复杂的事务处理,可能需要多个声明式事务进行组合
建议使用场景 应用需要局部控制事务,而非全局控制 应用需要全局控制事务
处理的业务比较复杂,需要编写更多的自定义逻辑 应用的事务需求相对简单

日常开发中我们多使用 @Transactional注解式事务管理,但在一些需要精细控制事务管理时往往力不从心,而被忽略的 编程式事务 就是解决此问题的利器。

三、使用 TransactionTemplate 进行编程式事务管理

TransactionTemplate是Spring框架提供的一个事务管理工具类,通过它我们可以手动控制事务的开始、提交和回滚等操作,方便我们更加灵活地处理事务,其主要提供了以下功能:

  • 在事务内执行一组操作
  • 设置事务的传播行为和隔离级别
  • 设置事务的超时时间
  • 优化只读事务

下面是一个简单的例子,演示了如何使用 TransactionTemplate 来实现编程式事务管理:

@Service
public class UserServiceImpl implements UserService {

    private final UserDao userDao;
    private final TransactionTemplate transactionTemplate;

    @Autowired
    public UserServiceImpl(UserDao userDao, TransactionTemplate transactionTemplate) {
        this.userDao = userDao;
        this.transactionTemplate = transactionTemplate;
    }

    @Override
    public void transferMoney(Long fromUserId, Long toUserId, Double amount) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    // 减少转出用户的余额
                    userDao.decreaseBalance(fromUserId, amount);
                    // 增加转入用户的余额
                    userDao.increaseBalance(toUserId, amount);
                } catch (RuntimeException e) {
                    // 出现异常时回滚事务
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });
    }
}
           

其它典型使用场景

1.在一个事务中执行多个操作

@Autowired
private TransactionTemplate transactionTemplate;

public void doMultipleOperations() {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            // 执行第一个操作
            someDao.insert(foo);
            // 执行第二个操作
            otherDao.update(bar);
            // 如果没有任何异常,则自动提交事务
        }
    });
}           

2.跨多个数据源执行分布式事务

@Autowired
@Qualifier("db1TransactionManager")
private PlatformTransactionManager db1TransactionManager;

@Autowired
@Qualifier("db2TransactionManager")
private PlatformTransactionManager db2TransactionManager;

public void doDistributedTransaction() {
    // 定义一个包含两个事务管理器的TransactionTemplate
    TransactionTemplate template = new TransactionTemplate();
    template.setTransactionManagers(db1TransactionManager, db2TransactionManager);

    // 在TransactionTemplate中执行跨多个数据源的操作
    template.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            someDao.insert(foo); // 在db1中执行操作
            otherDao.update(bar); // 在db2中执行操作
        }
    });
}           

3.处理同步/异步事务

@Service
public class UserServiceImpl implements UserService {
    
    private final TransactionTemplate transactionTemplate;
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    @Autowired
    public UserServiceImpl(DataSourceTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    @Override
    public void updateUser(User user) {
        // 同步事务
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    // 更新数据库操作
                    userDao.updateUser(user);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });

        // 异步事务
        executorService.submit(() -> {
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    try {
                        // 向外部系统发送消息
                        messageService.sendMessage(user.getName() + "更新了资料");
                    } catch (Exception e) {
                        status.setRollbackOnly();
                        throw e;
                    }
                }
            });
        });
    }
}           

在上面的例子中,我们使用了线程池来实现异步操作。在执行异步操作之前,我们先使用TransactionTemplate开启了一个同步事务,保证数据库操作与消息发送操作在同一事务中执行。接着,我们通过ExecutorService.submit()方法将消息发送操作提交到线程池中异步执行,同时在异步操作中再次使用TransactionTemplate开启了一个独立的事务。这样,我们就利用TransactionTemplate的传播行为机制,通过独立的事务管理异步操作,从而避免了多线程带来的线程安全问题,同时保证了数据的一致性和完整性。

继续阅读