Spring-事务

事务概述

  • 逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败
  • 也就是一套完整的业务代码,要么在出现问题的时候全部成功,要么全部失败

事务的特性

  • 原子性:事务不可被分割
  • 一致性:事务执行前,和执行后的数据是完整性的保持一致的
  • 隔离性:一个事务的执行不应该受到其他事务的干扰
  • 持久性:一旦事务结束,数据就会持久化的到数据库中
  • 如果不考虑 隔离性 会引发的安全性的问题
  • 🐸读问题
  • 脏读:一个事务读取到另一个事务中未提交的数据
  • 不可重复读:一个事务读取到另一个事务中已经提交的 update 的数据,导致一个事务中多次查询的结果不一致
  • 虚读幻读:一个事务读取到另一个事务中已经提交的 insert 的数据,导致一个事务中多次查询的结果不一致
  • 🐤写问题
  • 丢失更新

解决读问题

  • 设置事务的隔离级别
  • Read uncommitted:未提交读,任何读的问题都解决不了
  • Read committed:已提交读,解决了 脏读,但是 不可重复读虚读 有可能会发生
  • Repeatable read:重复读,解决了 脏读不可重复读,但是 虚读 有可能会发生
  • Serializable:解决所有读问题

Spring事务管理相关API

PlatformTransactionManager

  • PlatformTransactionManage:平台事务管理器是一个接口,下面有两个实现类,如下:
  • DataSourceTransactionManager:底层使用的是 JDBC管理事务
  • HibernateTransactionManager:底层使用是 Hibernate管理事务

TransactionDefinition

  • 事务定义信息:用于定义事务相关的信息如,隔离级别超时信息传播行为是否只读

TransactionStatus

  • 事务状态:用于记录在事务管理过程中,事务的状态

事务管理中API之间的关系

  • Spring 进行事务管理的时候,首先平台事务管理器(PlatformTransactionManager)根据事务定义信息(TransactionDefinition)进行事务的管理
  • 在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象(TransactionStatus)中

Spring事务的传播行为

  • 传播行为:一个业务方法当中,调用另一个业务的方法
  • Spring中提供了 7 种事务的传播行为,如下:

保证多个操作在同一个事务中

属性 作用
PROPAGATION_REQUIRED 默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
PROPAGATION_SUPPORTS 支持事务,如果A中有事务,使用A中的事务,如果A没有事务,不使用事务
PROPAGATION_MANDATORY 如果A中有事务,使用A中的事务,如果A没有事务,抛出异常

保证多个操作不在同一个事务中

属性 作用
PROPAGATION_REQUIRES_NEW 如果A中有事务,将A的事务挂起(暂停)创建新事务,只包含自身操作,如果A中没有事务,创建一个新事务,包含自身操作
PROPAGATION_NOT_SUPPORTED 如果A中有事务,将A的事务挂起,不使用事务管理
PROPAGATION_NEVER 如果A中有事务,报异常

嵌套式事务

属性 作用
PROPAGATION_NESTED 嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点
执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到之前设置的保存点

Spring事务管理

搭建Spring事务管理环境

  • 创建 AoccuntDao 接口,定义两个方法规范
/**
 * @author: BNTang
 **/
public interface AoccuntDao {
    void OutMoney(String from, BigDecimal money);
    void Inmoney(String to, BigDecimal money);
}
  • 实现 AoccuntDao 接口,创建 AoccuntDaoImpl 实现类,内容如下:
/**
 * @author: BNTang
 **/
public class AoccuntDaoImpl extends JdbcDaoSupport implements AoccuntDao {
    @Override
    public void OutMoney(String from, BigDecimal money) {
        this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money, from);
    }

    @Override
    public void Inmoney(String to, BigDecimal money) {
        this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);
    }
}
  • 修改 applicationContext.xml 将 Bean 交给 Spring 进行管理

<bean id="aoccuntDao" class="top.it6666.dao.impl.AoccuntDaoImpl"/>
  • 在 Dao 中注入数据源
  • 在 Dao 当中注入 JDBC模板,要保证 Dao 继承了 JdbcDaoSupport
  • 继承之后,就有了 DatasourceSet 方法了,就可以进行注入了

  • 改造 AoccuntDaoImpl

  • 修改 applicationContext.xml Dao 中注入 JDBC模板

...

<bean id="aoccuntDao" class="top.it6666.dao.impl.AoccuntDaoImpl">
    <property name="dataSource" ref="dataSource"/>
</bean>

...
  • 创建 AccountService 接口,定义业务方法规范
/**
 * @author: BNTang
 **/
public interface AccountService {
    void transferMoney(String from, String to, BigDecimal money);
}
  • 在创建 AccountServiceImpl 实现类,实现 AccountService 接口,并实现当中的方法
/**
 * @author: BNTang
 **/
public class AccountServiceImpl implements AccountService {

    private AoccuntDao aoccuntDao;

    public void setAoccuntDao(AoccuntDao aoccuntDao) {
        this.aoccuntDao = aoccuntDao;
    }

    @Override
    public void transferMoney(String from, String to, BigDecimal money) {
        aoccuntDao.OutMoney(from, money);
        aoccuntDao.Inmoney(to,money);
    }
}
  • 配置 Service 交给 Spring 并注入 Dao

...

<bean id="accountService" class="top.it6666.service.impl.AccountServiceImpl">
    <property name="aoccuntDao" ref="aoccuntDao"/>
</bean>

...
  • 测试类代码如下:
/**
 * @author: BNTang
 **/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {

    @Resource
    private AccountService accountService;

    @Test
    public void transferMoney() {
        accountService.transferMoney("BNTang", "JonathanTang", BigDecimal.valueOf(1000));
    }
}

添加事务

编程式事务

  • 需要手动编写代码
  • 配置平台事务管理器,修改 applicationContext.xml

...

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

...
  • Spring 提供了事务管理的模板类
  • 配置事务管理模板类,修改 applicationContext.xml

...

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

...
  • 在业务层注入事务管理模板
  • 修改 AccountServiceImpl 添加一个属性,待会用来注入用
/**
 * @author: BNTang
 **/
public class AccountServiceImpl implements AccountService {

    private AoccuntDao aoccuntDao;
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    
    ...
}
  • 修改 applicationContext.xml

...

<bean id="accountService" class="top.it6666.service.impl.AccountServiceImpl">
    <property name="aoccuntDao" ref="aoccuntDao"/>
    <property name="transactionTemplate" ref="transactionTemplate"/>
</bean>

...
  • 编写事务管理相关的代码,修改 AccountServiceImpl

/**
 * @author: BNTang
 **/
public class AccountServiceImpl implements AccountService {

    private AoccuntDao aoccuntDao;
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    public void setAoccuntDao(AoccuntDao aoccuntDao) {
        this.aoccuntDao = aoccuntDao;
    }

    @Override
    public void transferMoney(String from, String to, BigDecimal money) {
        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                aoccuntDao.OutMoney(from, money);
                int a = 1 / 0;
                aoccuntDao.Inmoney(to,money);
            }
        });
    }
}
  • 直接运行测试代码即可验证
  • 你可以先在有异常的情况下运行一次
  • 然后去掉异常运行一次即可验证

声明式事务

  • 配置事务管理器
  • 修改 applicationContext.xml

...

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

...
  • 开启支持使用注解的方式来进行管理事务
  • 修改 applicationContext.xml
  • 添加声明式事务注解的约束
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

<tx:annotation-driven transaction-manager="transactionManager"/>
  • 修改业务层代码如下:

/**
 * @author: BNTang
 **/
@Transactional
public class AccountServiceImpl implements AccountService {

    private AoccuntDao aoccuntDao;

    public void setAoccuntDao(AoccuntDao aoccuntDao) {
        this.aoccuntDao = aoccuntDao;
    }

    @Override
    public void transferMoney(String from, String to, BigDecimal money) {
        aoccuntDao.OutMoney(from, money);
        int a = 1 / 0;
        aoccuntDao.Inmoney(to, money);
    }
}
  • 测试类代码同上
  • 你可以先在有异常的情况下运行一次
  • 然后去掉异常运行一次即可验证
posted @ 2020-10-18 10:43  BNTang  阅读(90)  评论(0编辑  收藏  举报