从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

    • Spring介绍
    • 程序中的耦合
    • IoC的概念和作用
    • Spring中的依赖注入
    • 基于注解的IoC
    • 使用基于XML的IoC完成单表的CRUD
    • 使用基于注解的IoC完成单表的CRUD
    • IoC的纯注解配置
    • Spring整合Junit
    • 事务问题
    • 代理模式
    • AOP的概念和入门
    • 基于注解的AOP
    • Spring的JdbcTemplate
    • JdBCDaoSupport
    • 基于XML的AOP实现事务控制
    • 基于注解的AOP实现事务控制
    • Spring的事务控制
    • 基于XML的声明式事务控制
    • 基于注解的声明式事务控制
    • 纯注解实现事务控制
    • Spring编程式事务控制
      • 环境准备
      • 开始配置
      • 在service层配置
      • 缺点
      • 源码
    • Spring5新特性
    • Java
  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java并发

  • Java源码

  • JVM

  • 韩顺平

  • Java
  • Java
  • Spring
2023-05-08
目录

Spring编程式事务控制

# 180.Spring编程式事务控制

之前我们都是使用声明式的事务控制,通过配置的方式实现注解;现在讲讲如何通过编程式来实现控制,当然只是简单说说,了解即可,实际开发过程中使用的很少。

 ‍

# 环境准备

本次我们使用之前的案例:Spring的 事务控制 ,也就是基于demo17的分支进行开发

‍

# 开始配置

首先事务控制都离不开提交和回滚两个动作,所以即使是编程式事务控制,也得配置事务管理器

我们在bean.xml中添加如下内容:

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="dataSource"/>
</bean>
1
2
3
4
5

‍

Spring还给我们提供了一个事务模板对象TransactionTemplate:

<!-- 配置事务模版对象 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <!-- 注入事务管理器 -->
    <property name="transactionManager" ref="transactionManager"/>
</bean>
1
2
3
4
5

‍

TransactionTemplate类中有一个方法叫做execute:

@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;
	}
}

private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
	Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

	logger.debug("Initiating transaction rollback on application exception", ex);
	try {
		this.transactionManager.rollback(status);
	}
	catch (TransactionSystemException ex2) {
		logger.error("Application exception overridden by rollback exception", ex);
		ex2.initApplicationException(ex);
		throw ex2;
	}
	catch (RuntimeException | Error ex2) {
		logger.error("Application exception overridden by rollback exception", ex);
		throw ex2;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

‍

我们重点看第13行,有个action.doInTransaction​方法,并且执行后,如果有异常,就执行rollbackOnException方法回滚;在rollbackOnException方法中,执行的就是transactionManager.rollback(status);​方法,最后执行的也是connection对象的rollbank。

如果没有异常,则执行transactionManager.commit​方法,也就是提交,最后执行的也是connection对象的commit。

这个action是一个参数,其类型是TransactionCallback​,是一个接口。

接下来我们就在service层中配置。

‍

‍

# 在service层配置

我们在service实现类添加如下成员变量和set方法:

private TransactionTemplate transactionTemplate;

public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
    this.transactionTemplate = transactionTemplate;
}
1
2
3
4
5

‍

然后配置bean.xml:注入transactionTemplate对象

    <bean id="accountService" class="com.peterjxl.service.impl.AccountServiceImpl">
        <!-- 注入AccountDao -->
        <property name="accountDao" ref="accountDao"/>
        <!-- 注入TransactionTemplate -->
        <property name="transactionTemplate" ref="transactionTemplate"/>
    </bean>
1
2
3
4
5
6

‍

‍

然后我们在transfer方法中添加如下代码:也就是在模板对象的execute方法中执行事务,并且我们传入一个TransactionCallback​接口的匿名内部类:

transactionTemplate.execute(new TransactionCallback<Object>() {
    @Override
    public Object doInTransaction(TransactionStatus status) {
        return null;
    }
});
1
2
3
4
5
6

‍

然后我们将转账的代码中挪到execute方法里:

@Override
public void transfer(String sourceName, String targetName, Float money) {
    transactionTemplate.execute(new TransactionCallback<Object>() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
            // 2.1 根据名称查询转出账户
            Account source = accountDao.findAccountByName(sourceName);
            // 2.2 根据名称查询转入账户
            Account target = accountDao.findAccountByName(targetName);
            // 2.3 转出账户减钱
            source.setMoney(source.getMoney() - money);
            // 2.4 转入账户加钱
            target.setMoney(target.getMoney() + money);
            // 2.5 更新转出账户
            accountDao.updateAccount(source);

            int i = 1/0;
            // 2.6 更新转入账户
            accountDao.updateAccount(target);
            return null;
        }
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

‍

此时我们的transfer方法,就和模板对象的execute方法执行过程很类似。而我们自己的转账的业务逻辑代码,就相当于execute方法中,action.doInTransaction​方法内执行的内容。

如果有异常,就会回滚;没有异常才会提交。

然后我们可以测试下能否正常回滚事务,经过测试是可以控制住事务的

‍

# 缺点

综上,我们就是这样配置事务的,也就是service中的每个方法,都要这样配置:

public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public Account findAccountById(Integer accountId) {
        return transactionTemplate.execute(new TransactionCallback<Account>() {
            @Override
            public Account doInTransaction(TransactionStatus status) {
                return accountDao.findAccountById(accountId);
            }
        });
    }

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                // 2.1 根据名称查询转出账户
                Account source = accountDao.findAccountByName(sourceName);
                // 2.2 根据名称查询转入账户
                Account target = accountDao.findAccountByName(targetName);
                // 2.3 转出账户减钱
                source.setMoney(source.getMoney() - money);
                // 2.4 转入账户加钱
                target.setMoney(target.getMoney() + money);
                // 2.5 更新转出账户
                accountDao.updateAccount(source);

                int i = 1/0;
                // 2.6 更新转入账户
                accountDao.updateAccount(target);
                return null;
            }
        });
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

‍

至此,又多了很多重复代码,变的很不灵活

‍

# 源码

本项目已将源码上传到GitHub (opens new window)和Gitee (opens new window)上。并且创建了分支demo21,读者可以通过切换分支来查看本文的示例代码

在GitHub上编辑此页 (opens new window)
上次更新: 2023/5/11 14:10:59
纯注解实现事务控制
Spring5新特性

← 纯注解实现事务控制 Spring5新特性→

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式