Java实战之03Spring-05Spring中的事务控制(基于AOP)
五、Spring中的事务控制(基于AOP)
1、Spring中事务有关的接口
1.1、明确:
JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案
1.2、Spring事务管理主要包括3个接口
1.2.1、PlatformTransactionManager事务管理器
具体实现类:
1.2.2、TransactionDefinition事务定义信息
事务的隔离级别:
事务的传播行为:有时面试会问到
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
是否是只读事务:建议查询时设置为只读。
1.2.3、TransactionStatus事务具体运行状态
2、Spring中的事务控制
准备环境:
1.使用我们之前转账的案例。
2.使用Spring容器来管理业务层和持久层对象的注入
- public class AccountServiceImpl implements IAccountService {
- private IAccountDao accountDao ;
- public void setAccountDao(IAccountDao accountDao) {
- this.accountDao = accountDao;
- }
- public void transfer(String sourceName, String targetName, Float money) {
- //1.根据名称获取账户信息
- Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
- Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
- //2.设置账户的金额
- sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
- targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
- //3.更新账户信息
- accountDao.updateAccount(sourceAccount);
- //手动模拟一个异常
- int i=1/0;
- accountDao.updateAccount(targetAccount);
- }
- public Account findAccountById(Integer id) {
- return accountDao.findAccountById(id);
- }
- }
- public interface IAccountService {
- /**
- * 转账方法
- * @param sourceAccountName 转出账户名称
- * @param targetAccountName 转入账户名称
- * @param money 转账金额
- */
- void transfer(String sourceAccountName,String targetAccountName,Float money);
- /**
- * 根据id获取账户信息
- * @param id
- * @return
- */
- Account findAccountById(Integer id);
- }
3.使用Spring的jdbc模板(JdbcDaoSupport)来编写持久层。
- public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
- public Account findAccountByName(String accountName) {
- List<Account> list = getJdbcTemplate().query("select * from account where name = ?", new AccountRowMapper(),accountName);
- if(list.size() == 1){
- return list.get(0);
- }
- return null;
- }
- public void updateAccount(Account account) {
- getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
- }
- public Account findAccountById(Integer id) {
- List<Account> list = getJdbcTemplate().query("select * from account where id = ?", new AccountRowMapper(),id);
- if(list.size() == 1){
- return list.get(0);
- }
- return null;
- }
- }
- public class AccountRowMapper implements RowMapper<Account> {
- public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
- Account account = new Account();
- account.setId(rs.getInt("id"));
- account.setName(rs.getString("name"));
- account.setMoney(rs.getFloat("money"));
- return account;
- }
- }
- import cn.itcast.domain.Account;
- /**
- * 账户的持久层接口
- * @author zhy
- *
- */
- public interface IAccountDao {
- /**
- * 根据账户名称,查询账户信息
- * @param accountName
- * @return
- */
- Account findAccountByName(String accountName);
- /**
- * 更新账户信息
- * @param account
- */
- void updateAccount(Account account);
- /**
- * 根据id查询账户信息
- * @param id
- * @return
- */
- Account findAccountById(Integer id);
- }
4.使用Spring的内置数据源。
- <!-- 配置数据源 -->
- <bean id="driverManagerDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
- <property name="url" value="jdbc:mysql://localhost:3306/ee0413_spring_day37"></property>
- <property name="username" value="root"></property>
- <property name="password" value="1234"></property>
- </bean>
2.1、编程式事务控制(了解)
- 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;
- }
- public void transfer(final String sourceName,final String targetName,final Float money) {
- //定义一个TransactionCallback
- TransactionCallback action = new TransactionCallback<Account>() {
- //此方法内部,写需要事务控制的代码
- public Account doInTransaction(TransactionStatus status) {
- //1.根据名称获取账户信息
- Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
- Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
- //2.设置账户的金额
- sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
- targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
- //3.更新账户信息
- accountDao.updateAccount(sourceAccount);
- //手动模拟一个异常
- int i=1/0;
- accountDao.updateAccount(targetAccount);
- return null;
- }
- };
- //事务控制的方法
- transactionTemplate.execute(action);
- }
- public Account findAccountById(final Integer id) {
- TransactionCallback action = new TransactionCallback<Account>() {
- //此方法内部,写需要事务控制的代码
- public Account doInTransaction(TransactionStatus status) {
- return accountDao.findAccountById(id);
- }
- };
- //事务控制的方法
- return transactionTemplate.execute(action);
- }
- }
弊端:我们发现,其实就是编写了一个环绕通知,但是当我们业务代码很多时,每个都得向这样编写,很繁琐。
- <!-- 把业务层的创建交给spring容器 -->
- <bean id="accountService" class="cn.itcast.service.impl.AccountServiceImpl">
- <property name="accountDao" ref="accountDao"></property>
- <!-- 注入事务管理模板 -->
- <property name="transactionTemplate" ref="transactionTemplate"></property>
- </bean>
- <!-- 配置一个事务模板 -->
- <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
- <!-- 注入事务管理器 -->
- <property name="transactionManager" ref="transactionManager"></property>
- </bean>
- <!-- 配置事务管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <!-- 注入数据源 -->
- <property name="dataSource" ref="driverManagerDataSource"></property>
- </bean>
2.2、声明式事务控制(重点)
编程式事务管理将数据层提交事务的代码加入到逻辑层,与Spring无侵入式编程的主思想有冲突,实际开发过程中,往往采用声明式事务管理形式
通过编程式事务管理的代码不难看出,在业务逻辑层对应的业务上添加某些代码即可完成整体事务管理的操作,使用SpringAOP的思想,将公共的代码加入后,即可完成对应的工作,这就是声明式事务管理的核心机制。
2.2.1、基于XML的声明式事务配置
a、导入spring事务支持的jar包
b、引入spring事务的名称空间
c、配置spring的事务支持
- <!-- 配置事务管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <!-- 注入数据源 -->
- <property name="dataSource" ref="driverManagerDataSource"></property>
- </bean>
- <!-- 配置通知,通知中要引用事务管理器。因为事务管理器中有公共的代码:提交事务和回滚事务 -->
- <tx:advice id="txAdvice" transaction-manager="transactionManager">
- <!-- 事务的属性设置 -->
- <tx:attributes>
- <!-- 指定需要事务的方法
- 常用属性:
- isolation:指定事务的隔离级别
- propagation:指定事务的传播行为
- read-only:是否是只读事务
- timeout:事务的超时时间
- no-rollback-for:当产生指定异常时,不回滚。其他异常回滚。没有默认值
- rollback-for:当产生指定异常时回滚,产生其他异常时,不回滚。没有默认值
- -->
- <!-- 查询方法 -->
- <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
- <tx:method name="*" read-only="false" propagation="REQUIRED"/>
- </tx:attributes>
- </tx:advice>
- <!-- 指定通知和切入点表达式 -->
- <aop:config>
- <aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.service.impl.*.*(..))"/>
- </aop:config>
2.2.2、基于注解的声明式事务配置
a、把spring容器管理改为注解配置的方式
- <!-- 配置一个JdbcTemplate的bean -->
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="driverManagerDataSource"></property>
- </bean>
- public class AccountDaoImpl implements IAccountDao {
- @Autowired
- private JdbcTemplate jdbcTemplate;
- public Account findAccountByName(String accountName) {
- List<Account> list = jdbcTemplate.query("select * from account where name = ?", new AccountRowMapper(),accountName);
- if(list.size() == 1){
- return list.get(0);
- }
- return null;
- }
- public void updateAccount(Account account) {
- jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
- }
- public Account findAccountById(Integer id) {
- List<Account> list = jdbcTemplate.query("select * from account where id = ?", new AccountRowMapper(),id);
- if(list.size() == 1){
- return list.get(0);
- }
- return null;
- }
- }
- @Component("accountService")
- @Transactional
- public class AccountServiceImpl implements IAccountService {
- @Autowired
- private IAccountDao accountDao ;
- public void transfer(final String sourceName,final String targetName,final Float money) {
- //1.根据名称获取账户信息
- Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
- Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
- //2.设置账户的金额
- sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
- targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
- //3.更新账户信息
- accountDao.updateAccount(sourceAccount);
- //手动模拟一个异常
- int i=1/0;
- accountDao.updateAccount(targetAccount);
- }
- @Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
- public Account findAccountById(final Integer id) {
- return accountDao.findAccountById(id);
- }
- }
b、开启注解事务的支持
- <!-- 配置事务管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <!-- 注入数据源 -->
- <property name="dataSource" ref="driverManagerDataSource"></property>
- </bean>
c、在需要事务支持的地方加入@Transactional注解
- @Component("accountService")
- public class AccountServiceImpl implements IAccountService {
- @Autowired
- private IAccountDao accountDao ;
- @Transactional
- public void transfer(final String sourceName,final String targetName,final Float money) {
- //1.根据名称获取账户信息
- Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
- Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
- //2.设置账户的金额
- sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
- targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
- //3.更新账户信息
- accountDao.updateAccount(sourceAccount);
- //手动模拟一个异常
- int i=1/0;
- accountDao.updateAccount(targetAccount);
- }
- @Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
- public Account findAccountById(final Integer id) {
- return accountDao.findAccountById(id);
- }
- }
d、@Transactional注解配置的位置
l 用在业务实现类上:该类所有的方法都在事务的控制范围
l 用在业务接口上:该接口的所有实现类都起作用
l 通过注解指定事务的定义信息
Java实战之03Spring-05Spring中的事务控制(基于AOP)的更多相关文章
- Spring的事务控制-基于注解的方式
模拟转账操作,即Jone减少500,tom增加500 如果有疑问请访问spring事务控制-基于xml方式 1.创建数据表 2.创建Account实体类 public class Account { ...
- 代码中添加事务控制 VS(数据库存储过程+事务) 保证数据的完整性与一致性
做人事档案的系统考虑到数据的安全性与一致性,毕竟是要对外上线.真正投入使用的项目,数据库的可靠性与安全性上我们开发人员要考虑的就很多了,记得做机房收费系统时注册新卡是自己为了简单,写成了一个存储过程( ...
- Spring中的事务控制
Spring中事务控制的API介绍 PlatformTransactionManager 此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法 我们在开发中都是使用它的实现类: 真正 ...
- SmartSql使用教程(3)——SmartSql中的事务,及AOP的使用
一.引言 经过两章的铺垫,我们现在对SmartSql已经有了一定的了解,那么今天我们的主题是事务处理.事务处理是常用的一种特性,而SmartSql至少提供了两种使用事务的方法.一种是通过Reposit ...
- Spring的事务控制-基于xml方式
介绍:该程序模拟了转账操作,即Jone减少500元,tom增加500元 1.导入坐标 <dependency> <groupId>junit</groupId> & ...
- SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- SQL Server中的事务与其隔离级别之脏读, 未提交读,不可重复读和幻读
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- Django中的事务(Transaction)管理
Django默认的事务行为 默认情况下,在Django中事务是自动提交的.当我们运行Django内置的模板修改函数时,例如调用model.save()或model.delete()时,事务将被立即提交 ...
- lightning mdb 源代码分析(5)-事务控制
本博文系列前面已经探讨了LMDB的系统架构.MMAP映射.B-Tree操作等部分,本文将尝试描述LMDB中的事务控制的实现. 事务的基本特征: 事务是恢复和并发控制的基本单位.它是一个操作序列,这些操 ...
随机推荐
- TFS的使用
1.http://www.kwstu.com/ArticleView/kwstu_201462311500744
- .NET世界各成员之间的关系
相信看到这篇文章的人,心中肯定有这样的想法:ODBC.OLEDB.ADO.ADO.NET貌似都是访问数据库的东东,那么他们之间有什么区别,又有什么联系呢?不要着急,待我慢慢道来. 先说ODBC,官方的 ...
- 将SCOM2007代理升级到 System Center 2012 SP1
使用以下过程可以升级到 System Center 2012 Service Pack 1 (SP1), Operations Manager工程师.您应首先验证代理程序满足最小受支持的配置.有关详细 ...
- hdu4759 Poker Shuffle 2013 ACM/ICPC Asia Regional Changchun Online
找了很久的规律,只看十进制数字,各种乱七八糟的规律=没规律!看了别人的解题报告,虽然看懂了,可是怎么发现的这个规律呢T.T~想了很久很久~ 以下是转载的别人的图,自己再画太麻烦了~全部看出0~2n-1 ...
- [Javascript + lodash] sortBy and sortedIndex
sortBy: var collection = ['John', 'Petteri', 'Antti', 'Joonas', 'Zhentian']; var sorted = _.sortBy(c ...
- javascript中的一些偏门知识
undefined能够被重写 undefined = "now it's defined"; alert( undefined ); 浏览器测试结果: 浏览器 测试结果 结论 ie ...
- [JavaScript]转--如何让JS代码高大上
原文出处:http://www.cnblogs.com/wenber/p/3630373.html 1,创造简短的写法 你可以这么写: 1 var slice = Array.prototype.sl ...
- 应聘.net开发工程师常见的面试题(五)
1.描述一下C#中索引器的实现过程,是否只能根据数字进行索引? 答:不是.可以用任意类型. 2.在C#中,string str = null 与 string str = ” ” 请尽量使用文字或图象 ...
- 八、套接字(Socket)
demo 一个连接由它的两个端点标识,这样的端点称为套接 套接字是支持TCP/IP协议的网络通信的基本操作单元. 可以将套接字看作不同主机间的进程进行双向通信的端点. 上图连接1的一对套接字为: (1 ...
- C#_mvc_ajax_return data
假设cshtml文件中是这样的: <script type="text/javascript"> $(document).ready(function(){ $(&qu ...