• Spring–概念与模块
  • Spring–快速入门
  • Spring–基于IOC的CRUD操作
  • Spring–整合Junit
  • Spring–AOP面向切面编程
  • Spring–基于注解的AOP配置
  • Spring–JdbcTemplate基本使用
  • Spring–AOP实现事务控制
  • Spring–声明式事务控制

什么是事务

我们先以银行转账为例,假设user1要给user2转账1000元,这时转换为代码操作时,就是user1的账户中减少1000元,然后user2的账户中增加1000元。这时我们可以想象一种情况:如果在user1账户中减少1000元之后,出现异常,这时后续的user2账户增加1000元的方法就没有办法执行,平白无故的消失了1000元,在实际中这是不符合逻辑的。客观来说,转账应该是一组操作,应该出现的情况只能是一组增加,一组减少。在代码中,当中途出现异常时我们也应该做到取消转账的操作。我们可以把一整个转账的过程看作是一个事务。

事务的特性:

  • 原子性: 事务是一组操作组成的单元模块,执行操作时,要么操作成功,要么失败回滚到最开始的状态
  • 一致性: 事务执行前后数据的完整性必须保持一致
  • 隔离性: 多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰。多个并发事务的数据相互隔离
  • 持久性: 事务一旦被提交后,数据就会被持久化到数据库中

基于AOP的银行转账案例


要实现AOP事务控制,首先我们应该先准备配置相关的工具类:
ConnectionUtils: 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定

/*** @Author: Ly* @Date: 2020-07-31 17:58*/
public class ConnectionUtils {private ThreadLocal<Connection> tl=new ThreadLocal<Connection>();private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public void setTl(ThreadLocal<Connection> tl) {this.tl = tl;}/*** 获取当前线程上的连接* @return*/public Connection getThreadConnection(){try {//1.先从ThreadLocal上获取Connection conn=tl.get();//2.判断当前线程上是否有连接if(conn==null){//3.从数据源获取一个连接,并且存入ThreadLocal中conn=dataSource.getConnection();tl.set(conn);}//4.返回当前线程上的连接return conn;}catch (Exception e){throw  new RuntimeException(e);}}public void removeConnection(){tl.remove();}}

TransactionManager: 事务管理相关的工具类,包含:开启事务,提交事务,回滚事务和释放连接

/*** @Author: Ly* @Date: 2020-07-31 18:09*/
public class TransactionManager {private ConnectionUtils connectionUtils;public void setConnectionUtils(ConnectionUtils connectionUtils) {this.connectionUtils = connectionUtils;}/*** 开启事务*/public void beginTransaction(){try {connectionUtils.getThreadConnection().setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}/*** 提交事务*/public void commit(){try {connectionUtils.getThreadConnection().commit();} catch (SQLException e) {e.printStackTrace();}}/*** 回滚事务*/public void rollback(){try {connectionUtils.getThreadConnection().rollback();} catch (SQLException e) {e.printStackTrace();}}/*** 释放连接*/public void release(){try {connectionUtils.getThreadConnection().close();connectionUtils.removeConnection();} catch (SQLException e) {e.printStackTrace();}}}

配置bean.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--配置业务层对象Service--><bean id="accountService" class="com.ly.service.impl.AccountServiceImpl"><!--注入dao对象--><property name="accountDao" ref="accountDao"></property></bean><!--配置Data对象--><bean id="accountDao" class="com.ly.dao.impl.AccountDaoImpl"><!--注入QueryRunner--><property name="runner" ref="runner"></property><!--注入ConnectionUtils--><property name="connectionUtils" ref="connectionUtils"></property></bean><!--配置QueryRunner--><bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean><!--配置数据源--><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!--连接数据库的必备信息--><property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf-8"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean><!--配置Connection的工具类 ConnectionUtils--><bean id="connectionUtils" class="com.ly.utils.ConnectionUtils"><!--注入数据源--><property name="dataSource" ref="dataSource"></property></bean><!--配置事务管理器--><bean id="txManager" class="com.ly.utils.TransactionManager"><property name="connectionUtils" ref="connectionUtils"></property></bean><!--配置aop--><aop:config><!--配置通用切入点表达式--><aop:pointcut id="pt1" expression="execution(* com.ly.service.impl.*.*(..))"/><aop:aspect id="txAdvice" ref="txManager"><!--配置前置通知:开启事务--><aop:before method="beginTransaction" pointcut-ref="pt1"></aop:before><!--配置后置通知:提交事务--><aop:after-returning method="commit" pointcut-ref="pt1"></aop:after-returning><!--配置异常通知:回滚事务--><aop:after-throwing method="rollback" pointcut-ref="pt1"></aop:after-throwing><!--配置最终通知:释放连接--><aop:after method="release" pointcut-ref="pt1"></aop:after></aop:aspect></aop:config>
</beans>

AccountServiceImpl中添加相关的方法:

public void transfer(String sourceName, String targetName, Float money) {System.out.println("开始执行。。。");//2.执行操作//2.1.根据名称查询转出账户Account source=accountDao.findAccountByName(sourceName);//2.2.根据名称查询转入账户Account target=accountDao.findAccountByName(targetName);//2.3.转出账户减钱source.setMoney(source.getMoney()-money);int a=1/0;//2.4.转给账户加钱target.setMoney(target.getMoney()+money);//2.5.更新装出账户accountDao.updateAccount(source);//2.6.更新转入账户accountDao.updateAccount(target);}

测试转账案例:
首先我们先看我们数据库表中的数据:

执行测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {@Autowiredprivate IAccountService as;@Testpublic void testTransfer(){as.transfer("aaa","bbb",100f);}}

由于在转账过程中,转出账户后出现了/by zero错误,我们在看一下数据库表有没有发生改变。

数据表并没有发生改变,实现了回滚。

使用注解修改银行转账案例:
修改bean.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--配置spring创建容器时要扫描的包--><context:component-scan base-package="com.ly"></context:component-scan><!--配置QueryRunner--><bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean><!--配置数据源--><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!--连接数据库的必备信息--><property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf-8"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean><!--开启spring对注解AOP的支持--><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

在AccountServiceImpl中添加相关注解:

在AccountDaoImpl中添加相关注解:

在ConnectionUtils中添加相关注解:

在TransactionManager中添加相关注解:

@Component("txManager")
@Aspect
public class TransactionManager {@Autowiredprivate ConnectionUtils connectionUtils;@Pointcut("execution(* com.ly.service.impl.*.*(..))")private void pt1(){}/*** 开启事务*///@Before("pt1()")public void beginTransaction(){try {connectionUtils.getThreadConnection().setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}/*** 提交事务*///@AfterReturning("pt1()")public void commit(){try {connectionUtils.getThreadConnection().commit();} catch (SQLException e) {e.printStackTrace();}}/*** 回滚事务*///@AfterThrowing("pt1()")public void rollback(){try {connectionUtils.getThreadConnection().rollback();} catch (SQLException e) {e.printStackTrace();}}/*** 释放连接*///@After("pt1()")public void release(){try {connectionUtils.getThreadConnection().close();connectionUtils.removeConnection();} catch (SQLException e) {e.printStackTrace();}}@Around("pt1()")public Object aroundAdvice(ProceedingJoinPoint pjp) {Object rtValue = null;try {//1.获取参数Object[] args = pjp.getArgs();//2.开启事务this.beginTransaction();//3.执行方法rtValue = pjp.proceed(args);//4.提交事务this.commit();//返回结果return rtValue;} catch (Throwable throwable) {//5.回滚事务this.rollback();throw new RuntimeException(throwable);} finally {//6.释放资源this.release();}}}

这样基于注解的配置就完成了。

Spring--基于AOP实现事务控制相关推荐

  1. spring手动控制事务开启_Spring 基于AOP的事务控制

    What 事务是我们在进行数据操作的时候,要操作的事情,是一个完整的单元,不可再分的.它包括几个特性:原子性.一致性.可见性.隔离性: How 在Spring容器中,事务是被封装到事务管理器中,Spr ...

  2. Spring的三种事务控制

    Spring的三种方式的事务控制 1:基于编程式的事务控制 1.1:编程式事务控制相关对象 PlatformTransactionManager接口是spring的事务管理器,他里面提供了我们常用的操 ...

  3. 【spring】编程式事务控制

    结构: AccountServiceImpl package com.itheima.service.impl;import com.itheima.dao.IAccountDao; import c ...

  4. 从源码分析 Spring 基于注解的事务

    从源码分析 Spring 基于注解的事务 在spring引入基于注解的事务(@Transactional)之前,我们一般都是如下这样进行拦截事务的配置: <!-- 拦截器方式配置事务 --> ...

  5. 【Spring】基于注解实现事务控制(银行转账)

    结构 domain类 package com.itheima.domain;import java.io.Serializable;/*** 账户的实体类*/ public class Account ...

  6. 基于注解的AOP实现事务控制及问题分析

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  7. 【Spring】基于xml实现事务控制(银行转账)

    代码结构 domain类 package com.itheima.domain;import java.io.Serializable;/*** 账户的实体类*/ public class Accou ...

  8. 基于XML的AOP实现事务控制

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  9. Spring基于Annotation实现事务管理

    在 Spring 中,除了使用基于 XML 的方式可以实现声明式事务管理以外,还可以通过 Annotation 注解的方式实现声明式事务管理. 使用 Annotation 的方式非常简单,只需要在项目 ...

最新文章

  1. Python爬虫获取文章的标题及你的博客的阅读量,评论量。所有数据写入本地记事本。最后输出你的总阅读量!
  2. 在医学图像分析中使用ICP算法进行点云配准
  3. android方法中添加 N,Android N 新功能 - 添加快速设定
  4. 神经网络的梯度消失和过拟合产生原因及其解决方案
  5. Text Storage table
  6. 【学习笔记】第二章——调度算法:先来先服务FCFS、短作业优先SJF、高响应比HRRN
  7. 每天一个linux命令(60):scp命令
  8. EXCEL VBA编程入门四:录制宏
  9. php扩展引擎手册,模板引擎-THINKPHP 5.0 手册最新版
  10. 电脑总是弹出广告弹窗怎么办?
  11. java编程思想学习笔记——21多线程
  12. LaTeX调整页眉宽度适应文本
  13. android多行文本输入,android EditText多行文本输入的若干问题
  14. 将海康摄像机发布萤石云指南
  15. Windows下使用taskkill 命令结束进程
  16. 装X与务实并存!iPad越狱必装插件汇总
  17. Unity Shader-兰伯特光照模型与Diffuse Shader
  18. 蓝牙Sig Mesh 概念入门⑤——Mesh通信消息格式详解
  19. HTML/HTML5文本格式化标签:通过标签来美化文本外观
  20. Django的下载安装

热门文章

  1. Android高速下载器实现思路——单个任务的提速与优化
  2. 《JAVA》课程教学大纲
  3. 南非世界杯 小组赛 阿根廷vs尼日利亚
  4. Toronto Research Chemicals 对乙酰氧基苯乙酮说明书
  5. LikeLib跨链技术方案之哈希锁定
  6. 【C++】C++11新特性列表
  7. 学会提问 笔记(一)
  8. Gopro卡无法打开视频恢复方法
  9. RuntimeError: cannot cache function ‘__shear_dense‘: no locator available for file
  10. Qcon会议之所见所想