spring源码系列(十) 事物Transaction

1 预备知识

JDBC的使用及Spring + JDBC:
SpringJDBC源码解析 (看一下demo就行,源码不用看)

2 事物示例:

首先,config类上需要添加注解@EnableTransactionManagement:

@EnableTransactionManagement
@Configuration
public class JDBCConfig {

接着,类中需要配置事物管理器作为一个Bean:

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DruidDataSource druidDataSource) {
        return new DataSourceTransactionManager(druidDataSource);
    }

测试用例:

@Transactional
public class JDBCServiceImpl implements JDBCService{

    private JdbcTemplate jdbcTemplate;

    public JDBCServiceImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    @Override
    public void testTransactional() {
        jdbcTemplate.update("update user set name='王五' where id=1", new Object[]{});
        throw new RuntimeException("异常");
    }


public class JDBCEnter {
    public static void main (String args[]){
        ApplicationContext context = new AnnotationConfigApplicationContext("com.test.jdbc");
        JDBCService jdbcService=  context.getBean(JDBCService.class);
        jdbcService.testTransactional();
    }
}

3 spring 事物机制

spring的事物主要是通过PlatformTransactionManager 事务管理器来实现,事务管理器主要有三个API:

  • getTransaction(TransactionDefinition definition) ,获得事物。
  • commit(TransactionStatus status) 根据状态提交
  • rollback(TransactionStatus status) 根据状态回滚

此外,spring定义了事物的传播行为:

  • PROPAGATION_REQUIRED , required , 必须 【默认值】
    支持当前事务,A如果有事务,B将使用该事务。
    如果A没有事务,B将创建一个新的事务。
  • PROPAGATION_SUPPORTS ,supports ,支持
    支持当前事务,A如果有事务,B将使用该事务。
    如果A没有事务,B将以非事务执行。
  • PROPAGATION_MANDATORY,mandatory ,强制
    支持当前事务,A如果有事务,B将使用该事务。
    如果A没有事务,B将抛异常。
  • PROPAGATION_REQUIRES_NEW , requires_new ,必须新的
    如果A有事务,将A的事务挂起,B创建一个新的事务
    如果A没有事务,B创建一个新的事务
  • PROPAGATION_NOT_SUPPORTED ,not_supported ,不支持
    如果A有事务,将A的事务挂起,B将以非事务执行
    如果A没有事务,B将以非事务执行
  • PROPAGATION_NEVER ,never,从不
    如果A有事务,B将抛异常
    如果A没有事务,B将以非事务执行
  • PROPAGATION_NESTED ,nested ,嵌套
    A和B底层采用保存点机制,形成嵌套事务。

掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED

4 源码部分:

(跟着2.申明式事务的图走就行,从@EnableTransactionManagement开始看)
spring事务详解(三)源码详解

5 spring Transaction 使用ThreadLocal管理Connection

关于ThreadLocal,详见并发编程笔记中的第九点ThreadLocal部分:并发编程笔记:https://blog.csdn.net/bintoYu/article/details/86527400

事物Transaction有下面两个需求:

  1. 要求在一个方法内的所有方法都执行或都回滚,因此要确保某个线程在执行事物方法时,connection始终不变。
  2. 为了保证效率,可以让不同线程使用不同的connection。

spring借助ThreadLocal完美实现了这一机制。代码如下:

debegin()

PlatformTransactionManager 的doBegin()会获取数据库链接Connection,如果connection是新创建的,则将其绑定到ThreadLocal中。

    protected void doBegin(Object transaction, TransactionDefinition definition) {
        DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
        Connection con = null;

        try {
        	//1. 当前connection为空时,从dataSource里取出一个。
            if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                Connection newCon = this.dataSource.getConnection();
				...

                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }

           //2.对connection进行一些预处理,代码略
            ...

			//3. 如果是新的connection,则调用bindResouce()保存链接
            if (txObject.isNewConnectionHolder()) {
                TransactionSynchronizationManager.bindResource(this.getDataSource(), txObject.getConnectionHolder());
            }

        } catch (Throwable var7) {
			...
        }
    }

bindResource()

从下面的代码中可以看到,resources是一个ThreadLocal,存放的是一个Map结构,map中的key是dataSource,value是connection。这样就将connection存放到线程的ThreadLocal中了。

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<Map<Object, Object>>("Transactional resources");

	public static void bindResource(Object key, Object value) throws IllegalStateException {
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		Assert.notNull(value, "Value must not be null");
		//1. 从ThreadLocal中取出map
		Map<Object, Object> map = resources.get();
		//2. map为空的话,new一个并设置到ThreadLocal中
		if (map == null) {
			map = new HashMap<Object, Object>();
			resources.set(map);
		}
		//3. 将connection和dataSource放到map中
		Object oldValue = map.put(actualKey, value);
		...
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值