Spring开启事务流程和事务相关配置

Spring事务

Spring快速入门

事务作用:在数据层保障一系列的数据库操作同成功同失败

Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败

Spring提供了一个接口PlatformTransactionManager用来处理事务

public interface PlatformTransactionManager{
    
    
  	void commit(TransactionStatus status) throws TransactionException;
  	void rollback(TransactionStatus status) throws TransactionException;
}

PlatformTransactionManager接口最基本的事务管理实现类DataSourceTransactionManager

public class DataSourceTransactionManager {
    
    
    ……
}

案例学习: 模拟银行账户间转账业务

需求:实现任意两个账户间转账操作

需求微缩:A账户减钱,B账户加钱

分析:

①:数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)

②:业务层提供转账操作(transfer),调用减钱与加钱的操作

③:提供2个账号和操作金额执行转账操作

④:基于Spring整合MyBatis环境搭建上述操作

结果分析:

①:程序正常执行时,账户金额A减B加,没有问题

②:账户金额A减执行完成后, 程序出现异常后,转账失败,但是异常之前账户金额A减操作成功,异常之后账户金额B加操作失败,这是有非常大的安全隐患的

实现步骤:

首先创建一个数据库, 有id, name, money三个字段, 以及两条数据

create table tb_account(
    id int primary key auto_increment,
    name varchar(10),
    money varchar(10)
)

insert into tb_account (name, money)
values ("chenyq", 1000), ("zhangsan", 1000);

基于整合Mybatis和Junit环境(不会的可以看前面文章), 创建一个service层接口AccountService

public interface AccountService {
    
    
    /**
     * @param out 转出对象
     * @param in 转入对象
     * @param money 转出金额
     */
    void transfer(String out, String in, double money);
}

创建dao层接口AccountDao, 并定义一个加钱和减钱的方法

public interface AccountDao {
    
    
    @Update("update tb_account set money = money + #{money} where name = #{name};")
    void inMoney(@Param("name") String name, @Param("money") String money);
    
    @Update("update tb_account set money = money - #{money} where name = #{name};")
    void outMoney(@Param("name") String name, @Param("money") String money);
}

service层AccountService接口的AccountServiceImpl实现类调用数据层方法

@Service
public class AccountServiceImpl implements AccountService {
    
    
    @Autowired
    private AccountDao accountDao;

    public void transfer(String out, String in, double money) {
    
    
        accountDao.outMoney(out, money); // 账户减钱
        accountDao.inMoney(in, money); // 账户加钱
    }
}

测试类中进行测试, 可以看到数据库中是修改成功了的

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    
    
    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() {
    
    
        accountService.transfer("chenyq", "zhangsan", 100);
    }
}

在这里插入图片描述

下面我们在AccountServiceImpl实现类中模拟chenyq账户转钱出去之后, 程序出现异常

@Service
public class AccountServiceImpl implements AccountService {
    
    
    @Autowired
    private AccountDao accountDao;

    public void transfer(String out, String in, double money) {
    
    
        accountDao.outMoney(out, money); // 账户减钱
        int i = 1 / 0; // 模拟程序出现异常
        accountDao.inMoney(in, money); // 账户加钱
    }
}

重新执行测试类, 我们可以发现虽然程序报错, 但是数据库中chenyq账户的money又减少了100

在这里插入图片描述

我们需要开启Spring事务, 让加钱和减钱的操作同时成功或失败, 注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务, Spring开启事务步骤如下

步骤一: 使用@Transactional注解开启任务, 不过我们一般不是在实现类的方法中开启事务, 而是在接口的方法中开启, 降低耦合度

public interface AccountService {
    
    
    /**
     * @param out 转出对象
     * @param in 转入对象
     * @param money 转出金额
     */
    @Transactional // 开启事务
    void transfer(String out, String in, double money);
}

步骤二: 在JdbcConfig独立配置文件中, 定义设置一个事务管理器, 并将这个事务管理器交给Spring管理

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    
    
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    transactionManager.setDataSource(dataSource);
    return transactionManager;
}

步骤三: 在Spring核心配置文件中, 通过EnableTransactionManagement注解开启Spring对开启事务的注解支持(简单来说, 告知Spring是使用了注解开启事务的)

@Configuration
@ComponentScan("com.chenyq")
@PropertySource("classpath:jdbc.properties")
@Import({
    
    JdbcConfig.class, MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
    
    
}

到这事务也就开启完成了, 开启事务中的方法同成功同失败, 不会出现因为异常一个业务成功一个业务失败的情况

事务相关配置

事务有如下相关配置:

属性 作用 示例
readOnly 设置是否为只读事务 readOnly=true 只读事务
timeout 设置事务超时时间 timeout = -1(永不超时)
rollbackFor 设置事务回滚异常(class) rollbackFor = {NullPointException.class}
rollbackForClassName 设置事务回滚异常(String) 同上格式为字符串
noRollbackFor 设置事务不回滚异常(class) noRollbackFor = {NullPointException.class}
noRollbackForClassName 设置事务不回滚异常(String) 同上格式为字符串
propagation 设置事务传播行为 ……

使用示例:

@Transactional(readOnly = true, timeout = -1)
void transfer(String out, String in, double money);

有些异常是不参与事务回滚的, 我们需要rollbackFor设置这些异常参与事务回滚

@Transactional(rollbackFor = {
    
    NullPointException.class})
void transfer(String out, String in, double money);

如果我们在一个开启事务了的方法1中调用另一个方法2, 我们想要方法1事务回滚时, 方法2不回滚, 就需要设置事务多播行为

@Transactional(propagation = Propagation.REQUIRES_NEW)

事务传播行为还有其他属性

传播属性 事务管理员 事务协调员
REQUIRED(默认) 开启T 加入T
新建T2
REQUIRES_NEW 开启T 新建T2
新建T2
SUPPORTS 开启T 加入T
NOT_SUPPORTED 开启T
MANDATORY 开启T 加入T
ERROR
NEVER 开启T ERROR
NESTED 设置savePoint,一旦事务回滚,事务将回滚到savePoint处,交由客户响应提交/回滚

猜你喜欢

转载自blog.csdn.net/m0_71485750/article/details/127974104