事务管理

211 阅读5分钟
  • 介绍

  • 事务:一组业务操作ABCD,要么全部成功,要么全部不成功。

  • 特性:ACID

    • 原子性:整体
    • 一致性:完整
    • 隔离性:并发
    • 持久性:结果
  • 隔离问题:

    • 脏读:一个事务读到另一个事务没有提交的数据
    • 不可重复读:一个事务读到另一个事务已提交的数据(update)
    • 虚读(幻读):一个事务读到另一个事务已提交的数据(insert)
  • 隔离级别

    • read uncommitted:读未提交

    • read committed:读已提交

    • repeatable read:可重复读

    • serializable:串行化

  • mysql事务操作-基础

    Connection conn = null;
    try{
        //获得连接
        conn = ...;
        //开启事务
        conn.setAutoCommit(false);
        A
        B
        C
        D
        //提交事务
        conn.commit();
    }catch(){
        //回滚事务   
        conn.rollback();
    }
    
  • mysql事务操作-Savepoint

    • 需求:AB(必须),CD(可选)一个事务
    Connection conn = null;
    Savepoint savepoint = null;//保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
    try{
      //获得连接
      conn = ...;
      //开启事务
      conn.setAutoCommit(false);
      A
      B
      savepoint = conn.setSavepoint();
      C
      D
      //提交事务
      conn.commit();
    }catch(){
      //CD异常   
        if(savepoint != null){
            //回滚到CD之前
            conn.rollback(savepoint);
            conn.commit();
        }else{
            conn.rollback();
        }
    }
    
  • PlatformTransactionManager 平台事务管理器,spring要管理事务,必须使用事务管理器

    • 进行事务配置时,必须配置事务管理器
  • TransactionDefinition:事务定义(事务详情,事务属性),spring用于确定事务具体详情,

    • 例如:隔离级别,是否只读,超时时间
    • 进行事务配置时,必须配置详情。spring将配置项封装到该对象
  • TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成

    • spring底层根据状态进行相应操作
  • PlatformTransactionManager

    • DataSourceTransactionManager     //jdbc
      HibernateTransactionManager      //hibernate    
      
    //事务管理器通过“事务详情”,获取“事务状态”,从而管理事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //根据状态提交
    void commit(TransactionStatus var1) throws TransactionException;
    //根据状态回滚
    void rollback(TransactionStatus var1) throws TransactionException;
    
  • TransactionStatus

    public interface TransactionStatus extends SavepointManager, Flushable {
        boolean isNewTransaction(); //是否时新的事务
    
        boolean hasSavepoint();		//是否有保存点
    
        void setRollbackOnly();		//设置回滚
    
        boolean isRollbackOnly();	//是否回滚		
    
        void flush();		//刷新
    
        boolean isCompleted();	//是否完成
    }
    
  • TransactionDefinition

     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;//默认超时时间。默认值-1.使用数据库底层的超时时间
    
    int getPropagationBehavior();//传播行为
    
        int getIsolationLevel();//隔离级别
    
        int getTimeout();//获得超时时间
    
        boolean isReadOnly();//是否只读(增删改:读写,查询:只读)
    
        @Nullable
        String getName();//配置事务详情名称。一般方法名称。例如:save、add*等
    
    • 转播行为

      • 在业务之间如何来共享事务

         int PROPAGATION_REQUIRED = 0;//支持当前事务,A如果有事务,B将使用该事务。如果A没有事务,B将创建一个新的事务
         int PROPAGATION_SUPPORTS = 1;//支持当前事务,A如果有事务,B将使用该事务,如果A没有事务,B将以事务执行
        int PROPAGATION_MANDATORY = 2;//支持当前事务,A如果有事务,B将使用该事务,如果A没有事务,B将抛异常
        int PROPAGATION_REQUIRES_NEW = 3;//如果A有事务,将A的事务挂起,B创建一个新的事务,如果A没有事务,B创建一个新的事务
        int PROPAGATION_NOT_SUPPORTED = 4;//如果A有事务,将A的事务挂起,B将以非事务执行,如果A没有事务,B将以非事务执行
        int PROPAGATION_NEVER = 5;//如果A有事务,B将抛异常,如果A没有事务,B将以非事务执行
        int PROPAGATION_NESTED = 6;//A和B底层采用保存点机制,形成嵌套事务
        
      • 掌握

        • PROPAGATION_REQUIRED【默认值】
        • PROPAGATION_REQUIRES_NEW【创建新的事务】
        • ROPAGATION_NESTED【嵌套】
  • 案例:转账

    1. 搭建环境

      1. 创建表
         use spring_day02;
      
         create table account(
         	id int primary key auto_increment,
         	username varchar(50),
         	money int
         );
      
         insert into account(username,money) values('jeck',100);
         insert into account(username,money) values('吕敬瑛','10000');
      
    2. 导入jar包

      1. 核心:4+1
      2. aop:4(aop联盟,spring aop、aspectj规范、spring aspect)
      3. 数据库:2 (jdbc/tx)
      4. 连接池:c3p0
    3. dao

      public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
          @Override
          public void out(String outer, Integer money) {
              this.getJdbcTemplate().update("update account set money = money - ? where username = ?", money, outer);
          }
      
          @Override
          public void in(String inner, Integer money) {
              this.getJdbcTemplate().update("update account set money = money + ? where username = ?", money, inner);
          }
      }
      
    4. service

      public class AccountServiceImpl implements AccountService {
      
          private AccountDao accountDao;
      
          public void setAccountDao(AccountDao accountDao) {
              this.accountDao = accountDao;
          }
      
          @Override
          public void transfer(String outer, String inner, Integer money) {
              accountDao.in(inner,money);
              accountDao.out(outer,money);
          }
      }
      
    5. xml

      <!--数据源-->
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
          <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
          <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_day02"></property>
          <property name="user" value="root"></property>
          <property name="password" value="lv1117"></property>
      </bean>
      <!--dao-->
      <bean id="accountDao" class="com.adolph.dao.impl.AccountDaoImpl">
          <property name="dataSource" ref="dataSource"></property>
      </bean>
      <!--service-->
      <bean id="accountService" class="com.adolph.service.impl.AccountServiceImpl">
          <property name="accountDao" ref="accountDao"></property>
      </bean>
      
    6. 测试

      @Test
      public void dome(){
          String xmlPath = "com/adolph/applicationContext.xml";
          ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
          AccountServiceImpl accountService = applicationContext.getBean("accountService", AccountServiceImpl.class);
          accountService.transfer("lin","jeck",100);
      
      }
      
    7. 手动管理事务(了解)

      1. spring底层使用 TransactionTemplate事务模板进行操作

      2. 操作

        1. service需要获得TeansactionTemplate
        2. spring在配置模板,并注入给service
        3. 模板需要注入事务管理器
        4. 配置事务管理器:DataSourceTransactionManager
      3. 修改service

        //需要spring注入模板
        private TransactionTemplate transactionTemplate;
        
        public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
            this.transactionTemplate = transactionTemplate;
        }
        
        @Override
        public void transfer(final String outer, final String inner, final Integer money) {
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    accountDao.in(inner,money);
                    accountDao.out(outer,money);
                }
            });
        
        }
        
      4. 修改xml

        <!--service-->
        <bean id="accountService" class="com.adolph.service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
            <property name="transactionTemplate" ref="transactionTemplate"></property>
        </bean>
        
        <!--创建模板-->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="txManger"></property>
        </bean>
        
        <!--事务管理器,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得-->
        <bean id="txManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>