Spring系列:事务管理

Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring 是无法提供事务功能的。

一、Spring 事务传播机制

所谓 Spring 事务的传播机制,就是定义在存在多个事务同时存在的时候, Spring 应该如何处理这些事务的行为。

事务传播行为含义
PROPAGATION_REQUIRED0支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是Spring默认的事务的传播。
PROPAGATION_SUPPORTS1支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY2支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW3新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED4以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER5以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED6如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

二、Spring 事务机制

Spring并不会直接管理事务,而是提供了事务管理器,将事务管理的职责委托给JPA JDBC JTA DataSourceTransaction JMSTransactionManager 等框架提供的事务来实现。

Spring提供的事务管理器是 PlatformTransactionManager.java接口

2.1 PlatformTransactionManager.java

public interface PlatformTransactionManager{    
	// 通过Transation定义、获取Transation    
	TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws 	TransactionException;
	// 提交事务
	 void commit (TransactionStatus var1) throws TransactionException;
	// 回滚事务
	void rollback(TransactionStatus var1) throws TransactionException;
}

可以看到它里面引用到了 TransactionDefinitionTransactionStatus

2.2 TransactionDefinition.java

它里面包含了事务的定义。

public interface TransactionDefinition {
    // 传播机制
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    // 隔离级别
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;
    int getPropagationBehavior();
    // 获取隔离级别
    int getIsolationLevel();
    int getTimeout();
    boolean isReadOnly();
    @Nullable
    String getName();
}

2.3 TransactionStatus.java

事务的状态。

public interface TransactionStatus extends SavepointManager, Flushable {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    void flush();
    boolean isCompleted();
}

Spring 事务使用实践

假设我要完成一个功能,当删除用户的时候,将与该用户有关的数据行都删除。即要么同时成功,要么同时失败。

3.1 项目准备

新建用户表 user 及用户信息表 user_info,并插入两条数据。

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `user_name` varchar(100) NOT NULL COMMENT '账户名称',
  `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_update` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='用户表';

INSERT INTO `user` (`id`, `user_name`, `gmt_create`, `gmt_update`)
VALUES
	(1,'Van','2020-05-09 18:09:16',NULL),
	(2,'VanFan','2020-05-10 17:45:25',NULL);

DROP TABLE IF EXISTS `user_info`;

CREATE TABLE `user_info` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `user_id` int(11) NOT NULL COMMENT '用户id',
  `pass_word` varchar(100) NOT NULL COMMENT '登录密码',
  `nick_name` varchar(30) NOT NULL COMMENT '昵称',
  `mobile` varchar(30) NOT NULL COMMENT '手机号',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱地址',
  `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_update` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='用户信息表';

INSERT INTO `user_info` (`id`, `user_id`, `pass_word`, `nick_name`, `mobile`, `email`, `gmt_create`, `gmt_update`)
VALUES
	(1,1,'password','风尘博客','12580','110@qq.com','2020-02-09 18:05:27',NULL),
	(2,2,'password','公众号','110','12580@qq.com','2020-02-10 17:45:45',NULL);

Mapper 及其映射文件详见文末源码。

3.2 删除用户:不启用事物

@Test
public void delUserWithoutTransaction() {
    userInfoMapper.deleteUser(1L);
    if (true) {
        throw new RuntimeException("自造一个异常");
    }
    userInfoMapper.deleteUserInfo(1L);
}

执行测试方法,我们发现:userid = 1 的用户的数据删除了,但是在发生异常后,user_info 表的用户数据未删除,很明显是不符合要求的。

3.2 删除用户:事务注解

@Test
@Transactional
public void delUserWithAnnotationTransaction() {
    userInfoMapper.deleteUser(2L);
    if (true) {
        throw new RuntimeException("自造一个异常");
    }
    userInfoMapper.deleteUserInfo(2L);
}

代码跟上面完全一样,只是加了事务注解 @Transactional ,开启注解方式的事务后,执行发现:两个表中的 id = 2 的用户数据都删除失败,说明事务生效。

3.3 删除用户:手动开启

除了注解方式,我们还可以手动启用事务,如下:

@Test
public void delUserWithTransaction() {
    boolean executeRet = transactionTemplate.execute(transactionStatus -> {
        userInfoMapper.deleteUser(2L);
        if (true) {
            transactionStatus.setRollbackOnly();
            throw new RuntimeException("自造一个异常");
        }
        userInfoMapper.deleteUserInfo(2L);
        return true;
    });
}

四、总结

对于项目中需要使用到事务的地方,建议不要盲目使用 Spring 事务注解,尽量手动开启。

Spring 事务完整示例代码

4.1 日常求赞

博主祖传秘籍 Spring Boot 葵花宝典 开源中,欢迎前来吐槽,提供线索,告诉博主接下来更新哪方面文章,共同进步!

4.2 文化交流

  1. 风尘博客
  2. 风尘博客-掘金
  3. 风尘博客-博客园
  4. 风尘博客-CSDN
  5. Github

最新文章,欢迎关注:公众号-风尘博客;交流观点,欢迎添加:个人微信

风尘博客个人微信号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值