从@Transactional看事务的传播和隔离

文章目录

    • 1. transactionManager 当在配置文件中有多个TransactionManager,可以用该属性指定使用哪个事务管理器
    • 2. 事务的传播行为(propagation) ,默认值为REQUIRED
    • 3.事务的隔离( isolation)
    • 4. timeout 事务的超时时间 默认值为-1. 超时自动回滚
    • 5. readOnly 是否为只读事务,默认值为false,即非只读事务
    • 6. rollbackFor 指定能够触发事务回滚的异常类型 noRollbackFor 指定那些异常类型不回滚事务
  • 事务失效的场景

spring如何处理事务呢?下面是个伪代码示意:

	  begin Transactional;
       try{
           //TODO
           commit;
       }catch (Throwable e){
           if(e属于该提交的(即noRollbackFor指定)的异常类型){
               commit;
           }else {
               rollback;
           }
       }

1. transactionManager 当在配置文件中有多个TransactionManager,可以用该属性指定使用哪个事务管理器

如果要配置全局事务管理,参考这篇文章全局性事务控制如何在springboot中配置

2. 事务的传播行为(propagation) ,默认值为REQUIRED

  • Propagation.REQUIRED
    默认传播行为 如果有事务那么加入此事务,没有就新建一个事务
	/**
	 * Support a current transaction, create a new one if none exists.
	 * 

This is the default setting of a transaction annotation. */

  • Propagation.SUPPORTS
    如果其他bean调用这个方法,在其他bean中声明了事务那么久加入事务,如果其他bean中没有声明事务就不用事务
	/**
	 * Support a current transaction, execute non-transactionally if none exists.
	 * 

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. */

  • Propagation.REQUIRES_NEW
    不管是否存在事务,都创建一个新的事务。如果已经存在一个事务就停止他
	/**
	 * Create a new transaction, suspending the current transaction if one exists.
	 * 

NOTE: Actual transaction suspension will not work out-of-the-box * on all transaction managers. This in particular applies to * {@link org.springframework.transaction.jta.JtaTransactionManager}, * which requires the {@code javax.transaction.TransactionManager} to be * made available it to it (which is server-specific in standard Java EE). *

A {@code PROPAGATION_REQUIRES_NEW} scope always defines its own * transaction synchronizations. Existing synchronizations will be suspended * and resumed appropriately. */

  • Propagation.NOT_SUPPORTED
    不为这个方法开启事务
	/**
	 * Do not support a current transaction; rather always execute non-transactionally.
	 * 

NOTE: Actual transaction suspension will not work out-of-the-box * on all transaction managers. This in particular applies to * {@link org.springframework.transaction.jta.JtaTransactionManager}, * which requires the {@code javax.transaction.TransactionManager} to be * made available it to it (which is server-specific in standard Java EE). *

Note that transaction synchronization is not available within a * {@code PROPAGATION_NOT_SUPPORTED} scope. Existing synchronizations * will be suspended and resumed appropriately. */

  • Propagation.MANDATORY
    必须当前存在事务,否则抛出异常
	/**
	 * Support a current transaction, throw an exception if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
  • Propagation.NEVER
    必须当前没有事务,否则抛出异常,与Propagation.MANDATORY相反
	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
	 */
  • Propagation.NESTED
    如果当前存在事务,则在嵌套事务中执行,类似于ROPAGATION_REQUIRED
	/**
	 * Execute within a nested transaction if a current transaction exists,
	 * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
	 * 

Note: Actual creation of a nested transaction will only work on specific * transaction managers. Out of the box, this only applies to the JDBC */

嵌套事务中发生异常会回滚到savePoint,不对主事务之前的操作产生影响,但提交还要依赖主事务的成功。
从@Transactional看事务的传播和隔离_第1张图片

3.事务的隔离( isolation)

  • DEFAULT使用数据库默认的级别
    postgres数据库的默认隔离级别是已提交读 ,MySQL的默认事务隔离级别是可重复读。
  • READ_UNCOMMITTED 未提交读
	 A constant indicating that dirty reads, non-repeatable reads and phantom reads
	 * can occur. This level allows a row changed by one transaction to be read by
	 * another transaction before any changes in that row have been committed
	 * (a "dirty read"). If any of the changes are rolled back, the second
	 * transaction will have retrieved an invalid row.
  • READ_COMMITTED 已提交读
	 A constant indicating that dirty reads are prevented; non-repeatable reads
	 * and phantom reads can occur. This level only prohibits a transaction
	 * from reading a row with uncommitted changes in it.
  • REPEATABLE_READ 可重复读
		A constant indicating that dirty reads and non-repeatable reads are
	 * prevented; phantom reads can occur. This level prohibits a transaction
	 * from reading a row with uncommitted changes in it, and it also prohibits
	 * the situation where one transaction reads a row, a second transaction
	 * alters the row, and the first transaction rereads the row, getting
	 * different values the second time (a "non-repeatable read").
  • SERIALIZABLE 串行化
     A constant indicating that dirty reads, non-repeatable reads and phantom
	 * reads are prevented. This level includes the prohibitions in
	 * {@code ISOLATION_REPEATABLE_READ} and further prohibits the situation
	 * where one transaction reads all rows that satisfy a {@code WHERE}
	 * condition, a second transaction inserts a row that satisfies that
	 * {@code WHERE} condition, and the first transaction rereads for the
	 * same condition, retrieving the additional "phantom" row in the second read.

幻读和不可重复读相似容易混淆,幻读指的是第一个事务相同查询条件的查询行数,另一个事务增加或删除了某行(inserts a row),导致第一个事务两次查询的结果不同。不可重复读指的是另一个事务修改( alters the row)了某行的数据。

隔离和锁是不同的东西,隔离不是靠锁实现,是根据对数据的监控实现的,相比锁会回滚事务。

4. timeout 事务的超时时间 默认值为-1. 超时自动回滚

如果事务超过时间限制还没完成,就会回滚。
从方法执行开始计算。每个sql执行前检查一次是否超时,方法全部执行完毕后不检查是否超时。即设置事务超时为10秒,即使整个方法耗时20秒也不一定超时。

假设事务超时时间设置为2秒;假设sql执行时间为1秒;
如下调用是事务不超时的

public void testTimeout() throws InterruptedException {  
    System.out.println(System.currentTimeMillis());  
    JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);  
    jdbcTemplate.execute(" update test set hobby = hobby || '1'");  
    System.out.println(System.currentTimeMillis());  
    Thread.sleep(3000L);  
}  

而如下事务超时是起作用的:

public void testTimeout() throws InterruptedException {  
    Thread.sleep(3000L);  
    System.out.println(System.currentTimeMillis());  
    JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);  
    jdbcTemplate.execute(" update test set hobby = hobby || '1'");  
    System.out.println(System.currentTimeMillis());  
}  

參考博客

5. readOnly 是否为只读事务,默认值为false,即非只读事务

注意 在只读事务中修改数据库是会报错的!

6. rollbackFor 指定能够触发事务回滚的异常类型 noRollbackFor 指定那些异常类型不回滚事务

事务失效的场景

  1. 应用在非public方法上
  2. 同一个类中方法的调用
  3. 数据库引擎不支持事务,如innodb支持事务,而myisam就不支持事务
  4. 没有指定rollbackFor那些异常回滚时,spring默认的是运行时异常和error,如果不属于这些异常也就不会触发事务回滚。或者异常被catch掉了也不会触发事务回滚
  5. 传播机制设置为非事务方式

你可能感兴趣的:(数据库,springboot)