欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring事务的传播行为

程序员文章站 2022-05-06 21:37:47
...

      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种传播行为

      Spring事务的传播行为

      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的事务传播行为在开发中占据了很重要的地位。
相关标签: Spring 传播行为