Spring编程式事务TransactionTemplate与DataSourceTransactionManager

Spring编程式事务TransactionTemplate与DataSourceTransactionManager

Spring提供了@Transactional(rollbackFor = Exception.class)声明式事务,他非常的简单且好用,但是也有他失效的时候比如:
在多线程中 私有方法中 service中调用自己的方法这几种方式都会导致事务失效. 或者一些特殊的业务场景声明式事务无法满足
这时就要用Spring编程式事务编程式事务. 在这里主要介绍两种TransactionTemplate与DataSourceTransactionManager

DataSourceTransactionManager

上代码:


    /**
     * 测试编程式事务
     *
     * @throws Exception 异常
     */
    @Test
    public void testTransaction1() throws Exception {
     
        // 获取默认的事务
        TransactionStatus transactionStatus = this.dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
     
            SysUser sysUser = new SysUser();
            sysUser.setUsername(RandomStringUtils.randomAlphanumeric(6));
            sysUser.setNickname("22223");
            sysUser.setPassword("33333");
            sysUser.setSalt("123123");

            // 添加用户
            this.sysUserMapper.insert(sysUser);

            // 抛出异常事务进行回滚
            int i = 10 / 0;

            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setUserId(sysUser.getId());
            sysUserRole.setRoleId(1L);

            // 添加用户角色
            this.sysUserRoleMapper.insert(sysUserRole);
            // 事务提交
            this.dataSourceTransactionManager.commit(transactionStatus);
        } catch (Exception e) {
     
            log.error("添加用户时出现未知异常", e);
            // 事务回滚
            this.dataSourceTransactionManager.rollback(transactionStatus);
        }
    }

如果不加 int i = 10 / 0;除零异常

我们可以看到程序正常执行
数据库中的数据也是正常的
Spring编程式事务TransactionTemplate与DataSourceTransactionManager_第1张图片
在这里插入图片描述
一旦我们加了 int i = 10 / 0;除零异常
程序出现异常
Spring编程式事务TransactionTemplate与DataSourceTransactionManager_第2张图片
数据库中也不会插入数据
这就表示我们的编程式事务没有问题

在这里强调一下 一定在所有可能出现程序结束的地方加 事务的提交或回滚
在这里强调一下 一定在所有可能出现程序结束的地方加 事务的提交或回滚
在这里强调一下 一定在所有可能出现程序结束的地方加 事务的提交或回滚

重要的事情说三遍(因为本人 也因为事务没有回滚导致数据库死锁 的线上问题┭┮﹏┭┮)
一旦使用一定要谨慎
Spring编程式事务TransactionTemplate与DataSourceTransactionManager_第3张图片

TransactionTemplate

TransactionTemplate使用比DataSourceTransactionManager更加简单
上代码


    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    @Resource
    private TransactionTemplate transactionTemplate;


    /**
     * 测试编程式事务
     *
     * @throws Exception 异常
     */
    @Test
    public void testTransaction2() throws Exception {
     
        Boolean flag = this.transactionTemplate.execute(status -> {
     
        	// 业务代码块
            SysUser sysUser = new SysUser();
            sysUser.setUsername(RandomStringUtils.randomAlphanumeric(6));
            sysUser.setNickname("22223");
            sysUser.setPassword("33333");
            sysUser.setSalt("123123");

            // 添加用户
            this.sysUserMapper.insert(sysUser);
						
			// int i = 10 / 0;
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setUserId(sysUser.getId());
            sysUserRole.setRoleId(1L);

            // 添加用户角色
            this.sysUserRoleMapper.insert(sysUserRole);
            return Boolean.TRUE;
        });

        if (BooleanUtils.isNotTrue(flag)) {
     
            System.out.println("事务执行失败");
        }

    }

如果不加 int i = 10 / 0;除零异常

我们可以看到程序正常执行
数据库中的数据也是正常的

一旦我们加了 int i = 10 / 0;除零异常
程序出现异常
数据库中也不会插入数据
这就表示我们的编程式事务没有问题(同上我就不贴图了)

但是我们没有手动提交也没有手动回滚 他是怎么实现的呢
看源码:

	@Override
	@Nullable
	public <T> T execute(TransactionCallback<T> action) throws TransactionException {
     
		Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

		if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
     
			return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
		}
		else {
     
			TransactionStatus status = this.transactionManager.getTransaction(this);
			T result;
			try {
     
				// 着这里执行了我们的业务代码
				result = action.doInTransaction(status);
			}
			catch (RuntimeException | Error ex) {
     
				// 出现异常回滚
				// Transactional code threw application exception -> rollback
				rollbackOnException(status, ex);
				throw ex;
			}
			catch (Throwable ex) {
     
				// 出现异常回滚
				// Transactional code threw unexpected exception -> rollback
				rollbackOnException(status, ex);
				throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
			}
			// 没有异常提交
			this.transactionManager.commit(status);
			return result;
		}
	}

这就是我们没有手动控制事务 也可以进行事务的提交和回滚
手动控制事务回滚

    /**
     * 测试编程式事务
     *
     * @throws Exception 异常
     */
    @Test
    public void testTransaction2() throws Exception {
     
        Boolean flag = this.transactionTemplate.execute(status -> {
     
            SysUser sysUser = new SysUser();
            sysUser.setUsername(RandomStringUtils.randomAlphanumeric(6));
            sysUser.setNickname("22223");
            sysUser.setPassword("33333");
            sysUser.setSalt("123123");

            // 添加用户
            this.sysUserMapper.insert(sysUser);

            // 特殊的业务手动回滚
            int i = RandomUtils.nextInt();
            if (i % 2 == 0) {
     
                status.setRollbackOnly();
                return Boolean.FALSE;
            }

            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setUserId(sysUser.getId());
            sysUserRole.setRoleId(1L);

            // 添加用户角色
            this.sysUserRoleMapper.insert(sysUserRole);
            return Boolean.TRUE;
        });

        if (BooleanUtils.isNotTrue(flag)) {
     
            System.out.println("事务执行失败");
        }

    }

通过上下对比我们明显看出TransactionTemplate比DataSourceTransactionManager更加简单,而且不容易出错,所以在工作中个人还是比较推荐TransactionTemplate的.

注意:
无论是声明式事务还是编程式事务 再开一个线程 事务都不共享
无论是声明式事务还是编程式事务 再开一个线程 事务都不共享
无论是声明式事务还是编程式事务 再开一个线程 事务都不共享

你可能感兴趣的:(Spring,spring,java)