Spring事务的传播行为

      Spring的传播行为确定了客户端和被调用端的事务边界,简单来说就是多个具有事务控制的Service的相互调用时所形成的复杂的事务边界控制。Spring定义了7种传播行为,定义的枚举代码如下:

public enum Propagation {

	/**
	 * Support a current transaction, create a new one if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>This is the default setting of a transaction annotation.
	 */
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	/**
	 * Support a current transaction, execute non-transactionally if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: For transaction managers with transaction synchronization,
	 * PROPAGATION_SUPPORTS is slightly different from no transaction at all,
	 * as it defines a transaction scope that synchronization will apply for.
	 * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
	 * will be shared for the entire specified scope. Note that this depends on
	 * the actual synchronization configuration of the transaction manager.
	 */
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	/**
	 * Support a current transaction, throw an exception if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	/**
	 * Create a new transaction, and suspend the current transaction if one exists.
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p>Note: Actual transaction suspension will not work out-of-the-box on
	 * all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the {@code javax.transaction.TransactionManager} to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	/**
	 * Execute non-transactionally, suspend the current transaction if one exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: Actual transaction suspension will not work on out-of-the-box
	 * on all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the {@code javax.transaction.TransactionManager} to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	/**
	 * Execute within a nested transaction if a current transaction exists,
	 * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
	 * <p>Note: Actual creation of a nested transaction will only work on specific
	 * transaction managers. Out of the box, this only applies to the JDBC
	 * DataSourceTransactionManager when working on a JDBC 3.0 driver.
	 * Some JTA providers might support nested transactions as well.
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	NESTED(TransactionDefinition.PROPAGATION_NESTED);


	private final int value;


	Propagation(int value) { this.value = value; }

	public int value() { return this.value; }

}

7种传播行为

      

      NESTED与REQUIRES_NEW非常类似,如果调用者不存在一个事务则会开启一个新事务。如果存在一个事务时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚,两个事务互不影响,它不是一个真正的嵌套事务。同时需要JTA事务管理器的支持。使用NESTED时,外层事务的回滚可以引起内层事务的回滚,而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。

实例代码

      新建三张数据表,教师信息表、学生信息表和学生评价表:      

CREATE TABLE `Teacher` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `tno` int(11) NOT NULL COMMENT '工号',
  `name` varchar(10) NOT NULL DEFAULT '' COMMENT '姓名',
  `CreateTime` datetime NOT NULL COMMENT '记录插入时间',
  `UpdateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_idx_tno` (`tno`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;     
CREATE TABLE `Student` (
  `ID` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `Sno` int(11) NOT NULL COMMENT '学号',
  `Name` varchar(10) NOT NULL DEFAULT '' COMMENT '姓名',
  `Sex` tinyint(2) DEFAULT NULL COMMENT '0:女,1:男',
  `Grade` varchar(10) NOT NULL DEFAULT '' COMMENT '年级',
  `Clazz` tinyint(2) NOT NULL COMMENT '班级',
  `CreateTime` datetime NOT NULL COMMENT '记录插入时间',
  `UpdateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`ID`),
  UNIQUE KEY `unix_no` (`Sno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Evaluate` (
  `ID` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `Sno` int(11) DEFAULT NULL COMMENT '学号',
  `Name` varchar(10) NOT NULL DEFAULT '' COMMENT '姓名',
  `Comment` text NOT NULL COMMENT '评论',
  `Tno` int(11) DEFAULT NULL COMMENT '老师工号',
  `CommentDate` varchar(10) NOT NULL DEFAULT '' COMMENT '评论时间',
  `CreateTime` datetime NOT NULL COMMENT '记录插入时间',
  `UpdateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

REQUIRED和REQUIRED_NEW 

   创建一个方法,老师给学生添加评论的方法,实例代码如下:

 @Transactional(propagation = Propagation.REQUIRED)
    public boolean addEvaluateByTeacher(TeacherDO teacherDO, StudentDO studentDO, EvaluateDO evaluateDO) {
        Integer tno = teacherDao.insert(teacherDO);
        Integer sno = 0;
        try {
            sno = studentDao.insert(studentDO);
        } catch (Exception e) {
        }
        evaluateDO.setSno(sno);
        evaluateDO.setTno(tno);
        return evaluateDao.insert(evaluateDO) > 0 ? true : false;
    }
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public Integer insert(StudentDO studentDO) {
        if (studentDO == null) {
            LOGGER.error("insert student has error,param is null");
            return 0;
        }
        studentDOMapper.insert(studentDO);
        throw new IllegalArgumentException();
    }
     测试代码如下:

@Test
    public void addEvaluateByTeacher() throws Exception {
        TeacherDO teacherDO = new TeacherDO();
        teacherDO.setTno(5);
        teacherDO.setName("易中天");
        teacherDO.setCreateTime(new Date());
        StudentDO studentDO = new StudentDO();
        studentDO.setName("王晓鹏");
        studentDO.setSno(1111);
        studentDO.setClazz(5);
        studentDO.setSex(1);
        studentDO.setGrade("三年级");
        studentDO.setCreateTime(new Date());

        EvaluateDO evaluateDO = new EvaluateDO();
        evaluateDO.setName("王晓鹏");
        evaluateDO.setComment("好好学习,天天向上");
        evaluateDO.setCreateTime(new Date());
        evaluateDO.setCommentDate("2017-10-01");
        evaluateService.addEvaluateByTeacher(teacherDO, studentDO, evaluateDO);
    }
     执行结果如下:学生信息的insert方法由于使用“REQUIRES_NEW”的事务传播级别,执行时会创建新的事务,该事务和教师添加学生信息以及评论信息的方法addEvaluateByTeacher事务不是同一个事务,当学生信息的insert方法发生异常回滚时会单独回滚,不影响 addEvaluateByTeacher。所以,教师信息表和教师评论表可以插入成功,而学生信息表无法插入成功。

MANATORY

     此时我们修改上述的代码,学生信息的insert方法如下:

   @Transactional(propagation = Propagation.MANDATORY)
    public Integer insert(StudentDO studentDO) {
        if (studentDO == null) {
            LOGGER.error("insert student has error,param is null");
            return 0;
        }
        return studentDOMapper.insert(studentDO);
    }
    教师添加学生评论信息代码如下:    

 @Override
    public boolean addEvaluateByTeacher(TeacherDO teacherDO, StudentDO studentDO, EvaluateDO evaluateDO) {
        Integer tno = teacherDao.insert(teacherDO);
        Integer sno = 0;
        sno = studentDao.insert(studentDO);
        evaluateDO.setSno(sno);
        evaluateDO.setTno(tno);
        return evaluateDao.insert(evaluateDO) > 0 ? true : false;
    }
    此时执行单元测试会发生异常,因为事务传播级别MANDATORY的方法必须要求处于一个事务中,如果没有则会抛出异常。数据表中只有教师的信息插入成功,由于执行插入学生信息时抛出了异常,评论信息也会插入失败。执行结果如下:

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:359)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:463)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:276)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at com.sun.proxy.$Proxy19.insert(Unknown Source)
	at com.jj.tools.service.impl.EvaluateServiceImpl.addEvaluateByTeacher(EvaluateServiceImpl.java:32)
	at com.jj.tools.service.EvaluateServiceImplTest.addEvaluateByTeacher(EvaluateServiceImplTest.java:40)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
     关于事务的传播行为,暂时就写到这里,其他的传播行为读者可以一一去验证。在实际工作中,读者可以根据自己真实的业务场景去使用,Spring的事务传播行为在开发中占据了很重要的地位。
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring框架事务传播行为用于定义一个方法调用时如何参与到已存在的事务,或者如何创建一个新的事务Spring框架提供了多种事务传播行为选项,可以通过@Transactional注解或者编程式事务管理来配置。 以下是一些常见的Spring事务传播行为: 1. REQUIRED(默认):如果当前存在事务,则加入该事务;如果没有事务,则创建一个新的事务。这是最常用的传播行为,适合大多数情况。 2. SUPPORTS:如果当前存在事务,则加入该事务;如果没有事务,则以非事务的方式执行。适用于不需要强制要求事务的场景。 3. MANDATORY:如果当前存在事务,则加入该事务;如果没有事务,则抛出异常。适用于必须在一个已存在的事务执行的场景。 4. REQUIRES_NEW:创建一个新的事务,并挂起当前的事务(如果存在)。适用于需要独立的事务执行的场景。 5. NOT_SUPPORTED:以非事务的方式执行操作,挂起当前的事务(如果存在)。适用于不需要事务支持的场景。 6. NEVER:以非事务的方式执行操作,如果当前存在事务,则抛出异常。适用于必须在没有事务的环境下执行的场景。 7. NESTED:如果当前存在事务,则在嵌套事务执行;如果没有事务,则创建一个新的事务。嵌套事务是独立于外部事务的内部事务,它可以独立地进行提交或回滚,但是如果外部事务回滚,嵌套事务也会回滚。 通过选择合适的事务传播行为,可以确保在不同的方法调用有效地管理事务,保证事务的一致性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值