【spring入门】 事务 传播详解

配置方法

基于XML的配置

    <!--    配置事务管理器-->
    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--    配置事务属性-->
    <tx:advice id="myTx" transaction-manager="myTracnsactionManager">
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>  <!--具体方法-->
            <tx:method name="*" propagation="NEVER"/>       <!--剩下的方法-->
        </tx:attributes>
    </tx:advice>

    <!--    配置事务切入点,关联事务属性-->
    <aop:config>
        <aop:pointcut expression="execution(* org.example.service.ProductService.*(..))" id="txPointCut"/>
        
        <aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>
    </aop:config>

基于注解的配置
XML

    <bean id="myTracnsactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="myTracnsactionManager"/>
@Transactional()
  public void add(String name){}

@Transactional可选属性:

  Propagation propagation() default Propagation.REQUIRED; //传播方法

  Isolation isolation() default Isolation.DEFAULT; //隔离级别

  int timeout() default -1; //超时:单位秒,超过报错

  boolean readOnly() default false; //设置只读,事务优化

  Class<? extends Throwable>[] rollbackFor() default {}; //可回滚异常类(默认运行时异常回滚)

  String[] rollbackForClassName() default {}; //可回滚异常类名

  Class<? extends Throwable>[] noRollbackFor() default {};//不回滚异常类(默认checked异常)

  String[] noRollbackForClassName() default {}; //不回滚异常类名

传播详解

实验用到的几个类
Product

package org.example.pojo;

@Component("p")
public class Product {

  String name;

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

ProductDao

package org.example.dao;

@Repository
public class ProductDao extends JdbcDaoSupport {
  @Autowired
  public ProductDao(DataSource dataSource) {
    super();
    setDataSource(dataSource);
  }

  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }
}

ProductService

package org.example.service;

@Service
public class ProductService {
  @Autowired
  private ProductDao pd;

  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct(name);
    int a = 1/0;
  }
}

传播原理
在介绍七种传播行为前先看一个例子,稍微理解一下传播的原理。

@Repository
public class ProductDao extends JdbcDaoSupport {
  @Autowired
  public ProductDao(DataSource dataSource) {
    super();
    setDataSource(dataSource);
  }

  @Transactional
  public void addProduct(String name) throws Exception{
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
    int a = 1/0;
  }

  public void addProductWrapper(String name) throws Exception {
    addProduct(name);
  }
}
-----------------------------------------------------------------------------------
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ProductTest {
  @Autowired
  private ProductDao p;
  @Test
  public void testTx() throws Exception {
    p.addProduct("nihaoaa"); //情况1
    P.addProductWrapper("nihaoaa");  //情况2
  }
} 
 p.addProduct("nihaoaa");
 P.addProductWrapper("nihaoaa");

这两个结果一样吗???

结果:第二行代码没有回滚,在数据库中加了nihaoaa,第一行代码回滚了,没有在数据库中加东西。

原因:看起来addProductWrapper除了调用addProduct之外没有任何其他的行为,应该和直接执行addProduct是一样的。但是实际上,Spring通过AOP实现事务,Spring帮我们实现了一个ProductDao的代理Proxy,并在里面定义了新的addProduct,这个方法除了包括ProductDao.addProduct的所有功能外,还加上了事务。p.addProduct执行的是代理对象proxy中的addProduct,所以有回滚操作。而p.addProductWrapper在类内调用addProduct,实际调用的就是ProductDao.addProduct,即原始的addProduct。

接下来介绍七种传播行为。

传播属性当前有事务当前没有事务
propagation_requierd加入到当前事务中新建一个事务
propagation_required_new新建一个事务新建一个事务
propagation_supports加入到当前事务中以非事务方法执行
propagation_not_supported把当前事务挂起,以非事务方法执行以非事务方法执行
propagation_mandatory加入到当前事务中抛出异常
propagation_never抛出异常以非事务方法执行
propagation_nested在嵌套事务内执行新建一个事务

下面的测试都用这个代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ProductTest {
  @Autowired
  private ProductService ps;
  @Test
  public void testTx() throws Exception {
    ps.add("nihaoaa");
  }
} 
1.propagation_requierd
//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct(name);
    int a = 1/0;
  }
// ProductDao
  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:加入数据库失败。
service的add新建一个事务,ProductDao中的addProduct看到当前有事务了,就加入当前事务,所以尽管addProduct中没有异常,但因为add中有异常,也被回滚了。

//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct2(name+2);
    try{pd.addProduct(name);}
    catch (Exception e){
      System.out.println(Arrays.toString(e.getStackTrace()));
    }
    pd.addProduct2(name+3);
  }
// ProductDao
  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct(String name) throws Exception{
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
    int a = 1/0;
  }
  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct2(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:加入数据库失败
addProduct内的异常也会导致add中正常代码的回滚。

2.propagation_required_new
//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct(name);
    int a = 1/0;
  }
// ProductDao
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void addProduct(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:加入数据库成功
add的行为和REQUIRED的情况一样,addProduct看到当前事务存在了,仍然会创建一个新的事务,然后就在这个新事务里面运行,所以add事务里面的异常就和addProduct没有关系了。

//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct2(name+2);
    try{pd.addProduct(name);}
    catch (Exception e){
      System.out.println(Arrays.toString(e.getStackTrace()));
    }
    pd.addProduct2(name+3);
  }
//  ProductDao
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void addProduct(String name) throws Exception{
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
    int a = 1/0;
  }
  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct2(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:加入了nihaoaa2和nihaoaa3,没有加入nihaoaa。
因为addProduct和add是在两个不同的事务里面的,所以互相不会影响。

3.propagation_supports
//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct(name);
    int a = 1/0;
  }
// ProductDao
  @Transactional(propagation = Propagation.SUPPORTS)
  public void addProduct(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:加入数据库失败
原因和REQUIRED情况一样。

//  service
//  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct(name);
    int a = 1/0;
  }
// ProductDao
  @Transactional(propagation = Propagation.SUPPORTS)
  public void addProduct(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
    int a = 1/0;
  }

结果:加入数据库成功
此时add上没有事务了,addProduct看到没有当前事务,就以非事务方法执行了,所以尽管addProduct本身抛异常了,也不影响前面的数据库行为。

4.propagation_not_supported
//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct(name);
    int a = 1/0;
  }
// ProductDao
  @Transactional(propagation = Propagation.NOT_SUPPORTED)
  public void addProduct(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
    int a = 1/0;
  }

结果:加入数据库成功

//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct2(name+2);
    try{pd.addProduct(name);}
    catch (Exception e){
      System.out.println(Arrays.toString(e.getStackTrace()));
    }
    pd.addProduct2(name+3);
    int a = 1/0;
  }
// ProductDao
  @Transactional(propagation = Propagation.NOT_SUPPORTED)
  public void addProduct(String name) throws Exception{
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
    int a = 1/0;
  }
  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct2(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:加入了nihaoaa。
当事务存在时,把当前事务挂起,然后执行addProduct里面的非事务操作,所以加入了nihaoaa。然后add里面抛出异常之后回滚,但不会影响到addProduct里面的行为。

5.propagation_mandatory

这个很简单如果在add上定义,那就会报错,因为它被调用的时候没看到创建好的事务。
No existing transaction found for transaction marked with propagation ‘mandatory’。

6.propagation_never

这个也很简单,如果在addProduct上定义,并且add上定义了事务,报错。

7.propagation_nested

这个叫做嵌套事件,注意和propagation_required_new以及propagation_required的区别。

//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct2(name+2);
    try{pd.addProduct(name);}
    catch (Exception e){
      System.out.println(Arrays.toString(e.getStackTrace()));
    }
    pd.addProduct2(name+3);
    int a = 1/0;
  }
// ProductDao
  @Transactional(propagation = Propagation.NESTED)
  public void addProduct(String name) throws Exception{
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }
  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct2(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:什么都没有加入。
add创建了一个事务,addProduct在这个事务里面嵌套执行,因为add最后抛了个异常,所以整个事务回滚了,导致addProduct的行为也被回滚。

//  service
  @Transactional(propagation = Propagation.REQUIRED)
  public void add(String name){
    pd.addProduct2(name+2);
    try{pd.addProduct(name);}
    catch (Exception e){
      System.out.println(Arrays.toString(e.getStackTrace()));
    }
    pd.addProduct2(name+3);
  }
// ProductDao
  @Transactional(propagation = Propagation.NESTED)
  public void addProduct(String name) throws Exception{
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
    int a = 1/0;
  }
  @Transactional(propagation = Propagation.REQUIRED)
  public void addProduct2(String name){
    String sql = "insert into product(name) values(?)";
    this.getJdbcTemplate().update(sql,name);
  }

结果:加入了nihaoaa2和nihaoaa3。
addProduct里面的异常导致这段嵌入的行为被回滚,回滚到嵌入点(add调用addProduct的地方),然后add从嵌入点继续往下执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值