Spring 事务及传播属性

Spring 事务

JDBC原生事务实现

执行流程

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行sql
  4. 正常执行conn的commit()方法进行提交
  5. 异常回滚connection.rollback();

代码如下:

public class ConnectionUtil {
    public final static String DB_DRIVER_CLASS = "com.mysql.jdbc.Driver";
    public final static String DB_URL = "jdbc:mysql://127.0.0.1:3306/text";
    public final static String DB_USERNAME = "root";
    public final static String DB_PASSWORD = "123456";

    public static Connection getConnection() throws ClassNotFoundException,
            SQLException {
        Connection con = null;
        Class.forName(DB_DRIVER_CLASS);
        con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
        System.out.println("DB Connection created successfully");
        return con;
    }
}
public class JDBCTransationTest {
    public static void main(String[] args) {
        Connection connection = null;
        try {
            connection = ConnectionUtil.getConnection();
            //开启事务
            connection.setAutoCommit(false);
            // 插入数据
            insertTest(connection);
            // 提交事务
            connection.commit();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
            try {
               //回滚事务
                connection.rollback();
                System.out.println("JDBC Transaction rolled back successfully");
            } catch (SQLException e1) {
                System.out.println("JDBC Transaction rolled back fail" + e1.getMessage());
            }
        } finally {
         	// 释放资源
            if (connection != null) {
                try {
                    selectAll(connection);
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void insertTest(Connection con) throws SQLException {
        PreparedStatement stmt = con.prepareStatement("insert into test(num) values (?)");
        stmt.setString(1, "test2");
        stmt.executeUpdate();
        System.out.println("Data inserted successfully");
        stmt.close();
    }

Spring 事务管理

基本流程

Sprign事务是通过AOP去创建对应的事务管理器,其执行基本流程如下:

  1. 利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接
  2. 修改数据库连接的autocommit为false
  3. 执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql
  4. 如果没有抛异常,则提交
  5. 如果抛了异常,则回滚

事务原理

开启Spring事务本质上就是增加了一个Advisor,以使用@EnableTransactionManagement注解来开启Spring事务,该注解代理的功能就是向Spring容器中添加了两个Bean:
在这里插入图片描述
在这里插入图片描述

1、 AutoProxyRegistrar

在这里插入图片描述
在这里插入图片描述

AutoProxyRegistrar主要的作用是向Spring容器中注册了一个 InfrastructureAdvisorAutoProxyCreator的Bean。 而InfrastructureAdvisorAutoProxyCreator 继承了AbstractAdvisorAutoProxyCreator,所以这个类的主要作用就是开启自动代理的作用,也就是一个BeanPostProcessor,会在初始化后步骤中去寻找Advisor类型的Bean,并判断当前某个 Bean是否有匹配的Advisor,是否需要利用动态代理产生一个代理对象。

2、 ProxyTransactionManagementConfiguration

ProxyTransactionManagementConfiguration是一个配置类,它又定义了另外三个bean:
在这里插入图片描述

  1. BeanFactoryTransactionAttributeSourceAdvisor:一个Advisor =PointCut+Advice
    在这里插入图片描述
    在这里插入图片描述

  2. AnnotationTransactionAttributeSource:相当于 BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut 。
    用来判断某个类上是否存在@Transactional注解, 或者判断某个方法上是否存在@Transactional注解的
    在这里插入图片描述
    在这里插入图片描述

  3. TransactionInterceptor:相当于BeanFactoryTransactionAttributeSourceAdvisor中的 Advice。
    这是代理逻辑,当某个类中存在@Transactional注解时,到时就产生一个 代理对象作为Bean,代理对象在执行某个方法时,最终就会进入到TransactionInterceptor的 invoke()方法

Spring事务基本执行原理

一个Bean在执行Bean的创建生命周期时,会经过InfrastructureAdvisorAutoProxyCreator的初始化后的方法,会判断当前当前Bean对象是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配逻辑为判断该Bean的类上是否存在@Transactional注解,或者类中的某个方法上是否存在 @Transactional注解,如果存在则表示该Bean需要进行动态代理产生一个代理对象作为Bean对象。 该代理对象在执行某个方法时,会再次判断当前执行的方法是否和 BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配则执行该Advisor中的 TransactionInterceptor的invoke()方法

代理对象的生成匹配

Pointcut的实现一定是基于ClassFilter和MethodMatcher来处理
ClassFilter#matches()方法,则类匹配只是做了判断该Bean的类上是否存在@Transactional注解,并没有做其他处理
在这里插入图片描述
再看MethodMatcher##matches()方法,才是做真正的匹配
在这里插入图片描述
在这里插入图片描述
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);AOP工具方法,可以拿到真实的对象方法,
然后去查找方法是是否有@Transactional注解,有则封装成TransactionAttribute对象,没有则会判断类上是否有@Transactional注解,有则封装成TransactionAttribute对象,这里可以追踪到SpringTransactionAnnotationParser#parseTransactionAnnotation()方法
在这里插入图片描述
在这里插入图片描述
注意这里有一个判断,如果非public方法上@Transactional是不会生成代理的,也就是此时事务是会失效的
在这里插入图片描述

事务的执行

TransactionInterceptor的invoke()
在这里插入图片描述

在这里插入图片描述
基本执行流程如下:

1、利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接 ,修改数据库连接的autocommit为false,绑定线程和数据源对象和数据库连接对象关系
this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification)
其实这里面还有很对事务传播的判断,这里不做解释,后面的事务传播在细说明。
**2、执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql **
retVal = invocation.proceedWithInvocation();
**3、 如果没有抛异常,则提交 **
this.commitTransactionAfterReturning(txInfo);
4、 如果抛了异常,则回滚
this.completeTransactionAfterThrowing(txInfo, var18);

Spring事务失效情况
情况一:数据库不支持事务

Spring事务本身最终是反馈到数据的,所以如果数据库的执行引擎不支持事务的,该事务是无效的

情况二:事务没有交给spring管理

分为两种,一种是,需要加事务的对象没有加入spring容器中,另一种是由于spring是通过事务管理器控制事务的,如果没有创建事务管理器则也会失效

情况三:方法为非Public的

在方法匹配时我们看到,如果是非public是不会封装属性的,所以@Transactional 默认是只能用于 public 的方法上,否则事务会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

情况四:方法本地调用

spring操作事务是通过代理对象操作的,所以事务方法必须通过代理对象调用才会生效,否则就只是普通的方法调用

情况五:业务异常吞并

可以看到,事务管理是通过try…catch捕获异常才能做回滚的,如果业务代码的异常try…catch并没有向外抛出,这时出现异常后事务就不会回滚而使得事务的一致性没法满足,进而事务失效

情况六:rollback的异常不匹配

spring默认的是RuntimeException或者Error,如果业务中出现其他类型的异常,则也不会回滚,进而事务失效

情况七:事务声明传播属性为不支持事务:PROPAGATION_NOT_SUPPORTED

spring提供的一种传播机制,表示以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

Spring事务底层执行与传播机制

Spring事务传播属性和隔离级别
事务传播属性

spring支持的传播属性有7种,如下:在这里插入图片描述
1、PROPAGATION_REQUIRED=0
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择,也是默认的选择。
2、PROPAGATION_SUPPORTS=0
支持当前事务,如果当前没有事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY=2
使用当前的事务,如果当前没有事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW=3
新建事务,如果当前存在事务,把当前事务挂起。
5、PROPAGATION_NOT_SUPPORTED=4
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER=5
以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED=6
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

事务隔离级别

1、ISOLATION_DEFAULT=-1
默认的隔离级别,使用数据库默认的事务隔离级别.(另外四个与JDBC的隔离级别相对应)
2、 ISOLATION_READ_UNCOMMITTED=1:
这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。
3、ISOLATION_READ_COMMITTED=2:
保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据
4、ISOLATION_REPEATABLE_READ=4:
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
5、 ISOLATION_SERIALIZABLE=8
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。

脏读:
指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一 个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

不可重复读:
一个事务先后两次读取向同行的数据,发现数据不一致:事务1读取a行数据,事务2修改或者删除数据,事务1再次读取的数据和第一次不一样了。

幻觉读:
一个事务按照相同的查询条件重新读取数据,发现有新的数据插入:事务1按照条件a查询到b行记录,事务2再插入符合a条件的数据,当事务1再次按照条件a查询时发现多出了事务2插入的数据

事务执行与传播机制

回到TransactionInterceptor的invoke()追踪到TrasactionSupport#invokeWithinTransaction()

在这里插入图片描述

事务开启

1、事务开启createTransactionIfNecessary(),通过事务管理器创建事务
在这里插入图片描述
在这里插入图片描述
1.1、this.doGetTransaction(),获取事务管理器对象DataSourceTransactionManager.DataSourceTransactionObject,第一次进来只是创建了对象,数据源为null并且newConnectionHolder标识也为false,newConnectionHolder表示当前连接对象是否是新创建的标识,也就是只有第一次进来进行事务开启的时候才会设为true
在这里插入图片描述
顺带贴一下事务管理器的默认属性
在这里插入图片描述
1.2、isExistingTransaction是判断的当前是否有ConnectionHolder,显然第一次进来是不会走这里的逻辑
在这里插入图片描述
在这里插入图片描述
1.3、PROPAGATION_SUPPORTS=0,PROPAGATION_REQUIRES_NEW=3,PROPAGATION_NESTED=6,最终会到else,这里做的两件事,第一,挂起一个空事务suspend(null)
第二,开启事务startTransaction()方法
在这里插入图片描述
开启事务DefaultTransactionStatus status = this.newTransactionStatus(…),这里有一个重要属性newTransaction,为true,表示当前是一个新的事务,false表示当前已经开始了事务了
在这里插入图片描述
在这里插入图片描述
重点看 this.doBegin(transaction, definition),获取连接对象,关闭自动事务提交、绑定数据库连接对象,这里会设置newConnectionHolder标识为true。
在这里插入图片描述
然后prepareSynchronization(),事务状态扭转,这里也提供了一个工具类的使用TransactionSynchronizationManager,用来帮助业务过程中进行查询或设置当前事务的状态
在这里插入图片描述

业务方法执行

retVal = invocation.proceedWithInvocation()
调用到@transactional注解的真实的业务方法

事务提交

this.commitTransactionAfterReturning(txInfo);通过事务管理器提交事务操作
在这里插入图片描述
这里有很多判断,正常的提交则会走到this.processCommit(defStatus);
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

事务回滚

this.completeTransactionAfterThrowing(txInfo, var18),正常回滚,就是通过管理器获取连接对象进行事务回滚
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

事务传播属性控制
PROPAGATION_REQUIRED=0

传播特性:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中,也是spring默认的选择,就是整个只有一个事务
案例一:

@Component 
public class UserService { 
	@Autowired 
	private UserService userService; 
	@Transactional 
	public void test() { 
		// test方法中的sql 
		userService.a();
	 }
	 @Transactional 
	 public void a() {
	  	// a方法中的sql 
	  } 
  }

分析:
当执行test时,执行流程和正常的开启一样,会进行真正的事务开启,dobegin()方法,创建ConnectionHolder,设置newConnectionHolder标识为true,并建立threadLocal和数据源、线程池的映射关系,通过ConnectionHolder获取连接对象,设置自动提交为false表示事务开启。
然后执行到test方法中,userService.a()会触发代理调用执行到createTransactionIfNecessary()方法–》getTransaction(),如下:
在这里插入图片描述
此时,isExistingTransaction()为true,表示当前已经有事务了,PROPAGATION_REQUIRED = 0
根据传播属性判断执行如下:
在这里插入图片描述
这里其实什么都没做,就做了事务状态扭转,设置newTransaction为false表示当前不是新事物

然后invocation.proceedWithInvocation()执行a()方法
到this.commitTransactionAfterReturning(txInfo)提交事务,执行a()对应得提交时,这里会进行匹配判断,由于此时newTransaction为false,所以这里不会做真正的提交
在这里插入图片描述
然后a()结束,到test()方法执行完,提交则会真正的this.doCommit(status),进行提交。
最终流程为:

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行test方法中的sql
  4. 执行a方法中的sql
  5. 执行conn的commit()方法进行提交

案例二:

@Component 
public class UserService { 
	@Autowired 
	private UserService userService; 
	@Transactional 
	public void test() { 
	// test方法中的sql 
	userService.a(); 
	int result = 100/0; 
	}
	@Transactional 
	public void a() { 
	// a方法中的sql 
	} 
}

分析:
这里其实和案例一差不多,只不过,当执行到 result = 100/0时会抛异常,此时会被test的rollback,this.completeTransactionAfterThrowing(txInfo, var18)
在这里插入图片描述
追踪rollback()—>processRollback(),这里newTransaction为true,所以会this.doRollback(status)做真正的回滚,并向上抛出异常
在这里插入图片描述
最终流程为:

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行test方法中的sql
  4. 执行a方法中的sql
  5. 抛出异常
  6. 执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉

案例三:

@Component 
public class UserService { 
	@Autowired 
	private UserService userService; 
	@Transactional 
	public void test() { 
	// test方法中的sql 
	userService.a(); 
	}
	@Transactional 
	public void a() { 
	// a方法中的sql 
	int result = 100/0; 
	} 
}

分析:
test()方法事务开启同案例一一样,第二次进入a()事务切面设置newTransaction为false,此时有异常会到a()的事务回滚方法追踪到processRollback(),根据条件判断,则会走到
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里a方法的事务切面回滚,只会设置一个事务回滚属性rollbackOnly为true,然后向上抛出异常会被test()事务切面捕获异常,然后回滚动作就和案例二一样,最终流程:

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行test方法中的sql
  4. 执行a方法中的sql
  5. a方法抛出异常
  6. 执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉

案例四:

@Component 
public class UserService { 
	@Autowired 
	private UserService userService; 
	@Transactional 
	public void test() { 
	// test方法中的sql 
		try{
		userService.a();
		} catch (Exception e){
		
		}
	}
	@Transactional 
	public void a() { 
	// a方法中的sql 
	int result = 100/0; 
	} 
}

分析:
这里和案例三执行流程都差不多,但是当执行test()事务切面,尽管里面的a()方法异常了,但此时异常被业务方法自己捕获了,而并没有向上抛,也就是会走到test()事务切面的commit方法,
在这里插入图片描述
但这里有点不一样,案例三中可以看到a()回滚逻辑中设置了属性rollbackOnly为true,此时defStatus.isGlobalRollbackOnly()为true,this.shouldCommitOnGlobalRollbackOnly()默认为false,也就是此时test的提交逻辑并不会走真正的提交而是 this.processRollback(defStatus, true)进行强制回滚,而此时newTransaction为true,所以会真正的回滚
在这里插入图片描述
最终流程:

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行test方法中的sql
  4. 执行a方法中的sql
  5. a方法抛出异常 ,设置全局回滚标识
  6. 执行test事务切面的commit,但会强制rollback()方法进行回滚,所以两个方法中的sql都会回滚掉

案例五:

@Component 
public class UserService { 
	@Autowired 
	private UserService userService; 
	@Transactional 
	public void test() { 
	// test方法中的sql 
		
		userService.a();
		
	}
	@Transactional 
	public void a() { 
	// a方法中的sql 
	try{
	int result = 100/0; 
	} catch (Exception e){
		}
	} 
}

分析:
这里由于a()把异常吃掉了,并且并没有向上抛出,也就是正常执行,所以最终流程和案例一一样
执行流程:

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行test方法中的sql
  4. 执行a方法中的sql
  5. 执行conn的commit()方法进行提交
PROPAGATION_REQUIRES_NEW=3

新建事务,如果当前存在事务,把当前事务挂起。

案例六:

@Component 
public class UserService { 
	@Autowired 
	private UserService userService; 
	@Transactional 
	public void test() { 
	// test方法中的sql 
	userService.a(); 
	}
	@Transactional(propagation = Propagation.REQUIRES_NEW) 
	public void a() { 
	// a方法中的sql 
	int result = 100/0; 
	} 
}

分析:
执行到userService.a()前都和案例一一样,当执行a()时,根据判断事务开启,由于a()的传播属性PROPAGATION_REQUIRES_NEW=3,所以会执行如下
在这里插入图片描述
suspendedResources = this.suspend(transaction),将test()的事务挂起,挂起会将ConnectionHolder置空,同时将上一个事务的连接池对象与当前线程解绑,并封装成挂起资源对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

this.startTransaction(definition, transaction, debugEnabled, suspendedResources),创建新的连接对象,开启新事物事务,同时将上一个事务的资源挂在当前事务中,用于后续恢复事务。所以此时
a()事务和test()的事务不是同一个事务,newTransaction标识都为true.
当执行到a()方法中时,由于异常则会走a()事务切面的回滚,此时a()方法中的sql会先回滚,并且会向上抛出异常,然后来到a()事务切面的 this.commitTransactionAfterReturning(txInfo);追踪到processCommit(),
在这里插入图片描述
很明显,a()执行完后,有一个事务挂起的资源,所以此时会进行test()事务的恢复
在这里插入图片描述
恢复事务,也就是将挂起的连接对象设置到事务管理器,并绑定当前线程、数据源、连接对象的对应关系,
在这里插入图片描述
在这里插入图片描述
此时,由于test()破获到a()抛出的异常,也会走自己的回滚操作,最终都会回滚,
执行流程:

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行test方法中的sql
  4. 又新建一个数据库连接conn2
  5. 执行a方法中的sql
  6. 抛出异常
  7. 执行conn2的rollback()方法进行回滚
  8. 继续抛异常,对于test()方法而言,它会接收到一个异常,然后抛出
  9. 执行conn的rollback()方法进行回滚,最终还是两个方法中的sql都回滚了
PROPAGATION_NESTED=6

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

案例七:

@Component 
public class UserService { 
   @Autowired 
   private UserService userService; 
   @Transactional 
   public void test() { 
   // test方法中的sql 
   userService.a(); 
   userService.b(); 
   }
   @Transactional(propagation = Propagation.PROPAGATION_NESTED) 
   public void a() { 
   // a方法中的sql 
   } 

@Transactional(propagation = Propagation.PROPAGATION_NESTED) 
   public void b() { 
   // b方法中的sql 
   int result = 100/0; 
   } 
}

分析:
test()事务切面的执行与前面都一致,但当执行a()事务切面时,由于传播属性变成了PROPAGATION_NESTED=6,则会执行如下:
在这里插入图片描述
设置newTransaction标识为false,并且创建回滚点,status.createAndHoldSavepoint()
在这里插入图片描述
在这里插入图片描述
此时,并没有新创建新的连接对象,然后到a()正常执行,到a()事务的提交,由于有回滚点,所以并不会真正的提交,这里只做了回滚点的清除,也就是a()的回滚点擦除了
在这里插入图片描述
在这里插入图片描述
然后到b()事务切面,同a()一样也会创建回滚点,并且不会创建新的连接对象,也就是说test()、a()、b()是同一个连接对象,此时b()异常,这来到b()的事务回滚逻辑,如下:
在这里插入图片描述

回滚到回滚点,并设置强制回滚标识rollbackOnly为false,然后清除回滚点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
b()回滚逻辑完后,会抛出异常,这时会被test()事务破获到异常,就会来到test()的捕获逻辑,由于test()是创建事务的并且没有回滚点,测试走真正的回滚,这是test()、a()方法中的都会回滚,也就是全局回滚。
在这里插入图片描述
执行流程:

  1. 新建一个数据库连接conn
  2. 设置conn的autocommit为false
  3. 执行test方法中的sql
  4. 创建回滚点SAVEPOINT_1
  5. 执行a方法中的sql
  6. 擦除回滚点SAVEPOINT_1
  7. 创建回滚点SAVEPOINT_2
  8. 执行b中的方法
  9. 抛出异常
  10. 执行conn的rollback((Savepoint)savepoint)方法进行回滚
  11. 擦除回滚点SAVEPOINT_2
  12. 继续抛异常,对于test()方法而言,它会接收到一个异常,然后抛出
  13. 执行conn的rollback()方法进行回滚,最终还是test、a、b方法中的sql都回滚了

从上面流程看出,似乎回滚点没有遵循回滚,怎么能够保证回滚点呢?

从流程中可以看出,其实是因为内层方法的回滚没有做真正的回滚提交,因为真正的是交给外层回滚的,但我们发现它在回滚时,设置强制回滚标识rollbackOnly为false,从前面案例中,有结论:如果内层事务抛出异常,并且事务回滚时设置rollbackOnly为true,则表示需要强制回滚,如果为false则表示关闭强制回滚,也就是提交时会走真正的提交,所以我么可以将test的异常吃掉

代码如下:

@Component 
public class UserService { 
	@Autowired 
	private UserService userService; 
	@Transactional 
	public void test() { 
		try{
			// test方法中的sql 
			userService.a(); 
			userService.b(); 
		}catch(Exception e){
		}
	}
	@Transactional(propagation = Propagation.PROPAGATION_NESTED) 
	public void a() { 
	// a方法中的sql 
	} 

@Transactional(propagation = Propagation.PROPAGATION_NESTED) 
	public void b() { 
	// b方法中的sql 
	int result = 100/0; 
	} 
}
编程式事务

Spring 有声明式事务和编程式事务: 声明式事务只需要提供@Transactional 的注解,然后事务 的开启和提交/回滚、资源的清理就都由 spring 来管控,我们只需要关注业务代码即可;
编程式事务则需要使用 spring 提供的模板,如 TransactionTemplate,或者直接使用底层的 PlatformTransactionManager 手动控制提交、回滚。声明式事务的最大优点就是对代码的侵入性小, 只需要在方法上加@Transactional 的注解就可以实现事务; 编程式事务的最大优点就是事务的管控粒度较细,可以实现代码块级别的事务
如:

@Component
public class TransactionUtil {

    @Autowired
    private DataSourceTransactionManager transactionManager;

    /**
     * 开启事务
     *
     * @return:
     * @Author: th_legend
     **/
    public TransactionStatus begin() {
        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());

        return transaction;
    }

    public void testTransaction(){
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        // 开启事务
        TransactionStatus transactionStatus = begin(transactionDefinition);
        try {
            // 执行sql
            // 注册事务状态监听
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public int getOrder() {
                    return TransactionSynchronization.super.getOrder();
                }

                @Override
                public void suspend() {
                    System.out.println("事务挂起...");
                }

                @Override
                public void resume() {
                    System.out.println("事务恢复...");
                }

                @Override
                public void flush() {
                    TransactionSynchronization.super.flush();
                }

                @Override
                public void beforeCommit(boolean readOnly) {
                    System.out.println("事务准备提交...");
                }

                @Override
                public void beforeCompletion() {
                    System.out.println("事务准备提交或回滚...");
                }

                @Override
                public void afterCommit() {
                    System.out.println("事务提交完成...");
                }

                @Override
                public void afterCompletion(int status) {
                    System.out.println("事务提交活活滚完成...");
                }
            });
           xxx();
           commit(transactionStatus);
        }catch (Exception e){
            rollback(transactionStatus);
        }

    }

    /**
     * 开启事务
     *
     * @return:
     * @Author: th_legend
     **/
    public TransactionStatus begin(TransactionDefinition transactionDefinition) {
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        return transaction;
    }

    /**
     * 提交事务
     *
     * @return:
     * @Author: th_legend
     **/
    public void commit(TransactionStatus transaction) {
        if (null != transaction) {
            transactionManager.commit(transaction);
        }
    }

    /**
     * 回滚事务
     *
     * @return:
     * @Author: th_legend
     **/
    public void rollback(TransactionStatus transaction) {
        if (null != transaction) {
            transactionManager.rollback(transaction);
        }
    }

    /**
     * 获取数据源对象
     *
     * @return:
     * @Author: th_legend
     **/
    public DataSource getDataSource(){
      return   transactionManager.getDataSource();
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值