内容简介:Spring事务分为声明式事务(注解或包扫描)和编程式(在代码里提交或回滚)事务,声明式事务就是在编程式事务的基础上加上AOP计数进行包装这个工程为了实验事务的回滚,使用用了数据库,使用了jdbc模板连接数据库 ,数据库连接池配置再RootConfig里我导入的Maven依赖如下
Spring事务分为声明式事务(注解或包扫描)和编程式(在代码里提交或回滚)事务,声明式事务就是在编程式事务的基础上加上AOP计数进行包装
这个工程为了实验事务的回滚,使用用了数据库,使用了jdbc模板连接数据库 ,数据库连接池配置再RootConfig里
我导入的Maven依赖如下
<dependencies> <!-- 引入Spring-AOP等相关Jar --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.20.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.20.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.20.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.20.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.3.20.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.5.3</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_2</version> </dependency> <!--mysql连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <!--连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> 复制代码
配置类如下,用于代替有些过时的XML配置Spring
@Configuration @ComponentScan(basePackages = {"com.libi"}) @EnableAspectJAutoProxy public class RootConfig { @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/sms?userSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT"); dataSource.setUsername("root"); dataSource.setPassword("root"); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); return dataSource; } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } @Bean public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } } 复制代码
需要加入事务的方法如下userDao是会操作数据的,在中间的间隔会抛出异常
@Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; public void add() { userDao.add("test001","1233321"); System.out.println("中间的间隔,且出现异常"); int i = 1 / 0; userDao.add("test002","135365987"); } } 复制代码
这时只会插入test001的语句,test002不会插入成功。
编程式事务
这时我们封装一个事务工具
@Component @Scope("prototype") public class TransactionUtils { @Autowired private DataSourceTransactionManager dataSourceTransactionManager; private TransactionStatus status; /** 开启事务*/ public TransactionStatus begin() { //使用默认的传播级别 TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } /** 提交事务 需要传入这个事务状态*/ public void commit() { dataSourceTransactionManager.commit(status); } /**回滚事务 需要传入这个事务状态*/ public void rollBack() { //获取当前事务,如果有,就回滚 if (status != null) { dataSourceTransactionManager.rollback(status); } } } 复制代码
再这样使用,修改add方法
public void add() { TransactionStatus begin = null; try { begin = transactionUtils.begin(); userDao.add("test001", "1233321"); System.out.println("中间的间隔,且出现异常"); int i = 1 / 0; userDao.add("test002", "135365987"); transactionUtils.commit(); } catch (Exception e) { e.printStackTrace(); transactionUtils.rollBack(); } } 复制代码
声明式事务
我们使用AOP编程把刚刚的事务 工具 封装一下
@Component @Aspect public class AopTransaction { @Autowired private TransactionUtils transactionUtils; @Around("execution(* com.libi.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("开启事务"); proceedingJoinPoint.proceed(); System.out.println("提交事务"); transactionUtils.commit(); } @AfterThrowing("execution(* com.libi.service.UserService.add(..))") public void afterThrowing() { System.out.println("回滚事务"); //获取当前事务,直接回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } 复制代码
然后清空原来的方法里所有的try代码,让他回到最初的状态( 不能捕获异常,否者出现异常后不能被异常通知捕获到,导致事务不生效 )
注解式事务
在Spring里已经帮我们实现类注解事务,需要在配置类里添加下面的注解来开启注解事务的支持
@EnableTransactionManagement 复制代码
然后 注释掉我们上次的AOP注解
,使用 @Transactional(rollbackFor = Exception.class)
的注解开启这个方法的事务, rollbackFor
标识需要回滚的异常类,整个方法如下
@Transactional(rollbackFor = Exception.class) public void add() { userDao.add("test001", "1233321"); System.out.println("中间的间隔,且出现异常"); int i = 1 / 0; userDao.add("test002", "135365987"); } 复制代码
这样也可以实现这个方法的事务。 当然,这个方法里也不能捕获异常,这样仍然会导致无法触发异常通知而导致事务无效
我们就以这种效果作为模板手写事务的框架
具体步骤
- 定义注解
/** * @author libi * 自己实现的事务注解 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ExtTransaction { } 复制代码
- 封装手动事务(使用原来的TransactionUtils类)
-
使用AOP扫描规定包下的注解
- 在AOP上封装找到注解并且加上注解的操作
@Component @Aspect public class AopAnnotationTransaction { @Autowired private TransactionUtils transactionUtils; /**这边规定扫描service下的所有方法*/ @Around("execution(* com.libi.service.*.*(..))") //获取方法上的注解,这里把获取注解的方法单独提出来了 ExtTransaction extTransaction = getExtTransaction(proceedingJoinPoint); TransactionStatus status = null; if (extTransaction != null) { //若果有事务,开启事务 System.out.println("开启事务"); status = transactionUtils.begin(); } //调用代理目标方法 proceedingJoinPoint.proceed(); if (status != null) { //提交事务 System.out.println("提交事务"); transactionUtils.commit(); } } /**事务的异常通知*/ @AfterThrowing("execution(* com.libi.service.*.*.*(..))") public void afterThrowing() { System.out.println("回滚事务"); transactionUtils.rollBack(); } /**获取方法上的注解*/ private ExtTransaction getExtTransaction(ProceedingJoinPoint proceedingJoinPoint) throws NoSuchMethodException { //获取代理对象的方法 String methodName = proceedingJoinPoint.getSignature().getName(); Class<?> targetClass = proceedingJoinPoint.getTarget().getClass(); Class[] parameterTypes = ((MethodSignature) (proceedingJoinPoint.getSignature())).getParameterTypes(); Method targetMethod = targetClass.getMethod(methodName, parameterTypes); //获取方法上的注解 return targetMethod.getAnnotation(ExtTransaction.class); } } 复制代码
还要注意的是,TransactionUtils类仍然需要时多例的,不然会出现线程安全问题
事务传播行为
- 什么是传播行为(Propagation) :事务的传播行为产生在调用事务中,也就是说当小个事务嵌套在大事务里时,会发生怎样的行为
-
传播行为的种类
- PROPAGATION_REQUIRED—如果当前有事务,就用当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。( 如果大的方法有事务,那么需要事务的小方法就加入到这个事务里去,如果大方法没有事务,就创建事务 )
- PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。//( 如果外层方法没有事务,就会以非事务进行执行。这样相当于默认没有事务 )
- PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起( 互不影响,运行到小事务时暂停大事务 )。
- PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- --- 如果当前有事务,就是以非事务进行执行
- PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 手写源码(四):自己实现Mybatis
- 手写源码(三):自己实现SpringMVC
- 手写源码(二):自己实现SpringIOC
- EventBus 源码详细分析:手写 EventBus 核心代码
- 阿里架构师手写Tomcat——Session源码解析
- node进阶——之事无巨细手写koa源码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。