第5章Spring 事务(测试)-Spring的事务注解(小项目中),AspectJ的AOP配置管理事务(大项目中)
spring框架中提供的事务处理方案
适合中小项目使用的, 注解方案。
- 1.适合中小项目使用的, 注解方案。
- spring框架自己用aop实现给业务方法增加事务的功能, 使用@Transactional注解增加事务。
- @Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。
- 可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等
-
- 使用@Transactional的步骤:
- 1.需要声明事务管理器对象
-
"xx" class="DataSourceTransactionManager">
1. 使用 Spring 的事务注解管理事务(掌握)
通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。
- @Transactional 的所有可选属性如下所示:
- ➢ propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
- ➢ isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT。
- ➢ readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
- ➢ timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
- ➢ rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
- ➢ rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
- ➢ noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
- ➢ noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。
- 当然,若只有一个异常类时,可以不使用数组。
- 需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public
- 方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该
- 方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。
- 若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
-
实现注解的事务步骤:
-
声明事务管理器
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="myDataSource" />
- bean>
-
开启注解驱动
- <tx:annotation-driven transaction-manager="transactionManager" />
-
业务层 public 方法加入事务属性
- @Transactional(
- propagation = Propagation.REQUIRED,
- isolation = Isolation.DEFAULT,
- readOnly = false,
- rollbackFor = {
- NullPointerException.class, NotEnoughException.class
- }
-
-
此时的spring配置文件:
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- https://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd">
- <context:property-placeholder location="classpath:jdbc.properties" />
- <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
- init-method="init" destroy-method="close">
- <property name="url" value="${jdbc.url}" />
- <property name="username" value="${jdbc.username}"/>
- <property name="password" value="${jdbc.passwd}" />
- <property name="maxActive" value="${jdbc.max}" />
- bean>
- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="myDataSource" />
- <property name="configLocation" value="classpath:mybatis.xml" />
- bean>
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
- <property name="basePackage" value="com.bjpowernode.dao"/>
- bean>
- <bean id="buyService" class="com.bjpowernode.service.impl.BuyGoodsServiceImpl">
- <property name="goodsDao" ref="goodsDao" />
- <property name="saleDao" ref="saleDao" />
- bean>
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="myDataSource" />
- bean>
- <tx:annotation-driven transaction-manager="transactionManager" />
- beans>
-
com.bjpowernode.service.impl.BuyGoodsServiceImpl
- package com.bjpowernode.service.impl;
- import com.bjpowernode.dao.GoodsDao;
- import com.bjpowernode.dao.SaleDao;
- import com.bjpowernode.domain.Goods;
- import com.bjpowernode.domain.Sale;
- import com.bjpowernode.excep.NotEnoughException;
- import com.bjpowernode.service.BuyGoodsService;
- import org.springframework.transaction.annotation.Isolation;
- import org.springframework.transaction.annotation.Propagation;
- import org.springframework.transaction.annotation.Transactional;
- public class BuyGoodsServiceImpl implements BuyGoodsService {
- private SaleDao saleDao;
- private GoodsDao goodsDao;
- /**
- *
- * rollbackFor:表示发生指定的异常一定回滚.
- * 处理逻辑是:
- * 1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
- * 如果异常在rollbackFor列表中,不管是什么类型的异常,一定回滚。
- * 2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,
- * 如果是一定回滚。
- *
- */
- /* @Transactional(
- propagation = Propagation.REQUIRED,
- isolation = Isolation.DEFAULT,
- readOnly = false,
- rollbackFor = {
- NullPointerException.class, NotEnoughException.class
- }
- )*/
- //使用的是事务控制的默认值, 默认的传播行为是REQUIRED,默认的隔离级别DEFAULT
- //默认抛出运行时异常,回滚事务。
- @Transactional(rollbackFor=Exception.class)
- @Override
- public void buy(Integer goodsId, Integer nums) {
- System.out.println("=====buy方法的开始====");
- //记录销售信息,向sale表添加记录
- Sale sale = new Sale();
- sale.setGid(goodsId);
- sale.setNums(nums);
- saleDao.insertSale(sale);
- //更新库存
- Goods goods = goodsDao.selectGoods(goodsId);
- if( goods == null){
- //商品不存在
- throw new NullPointerException("编号是:"+goodsId+",商品不存在");
- } else if( goods.getAmount() < nums){
- //商品库存不足
- throw new NotEnoughException("编号是:"+goodsId+",商品库存不足");
- }
- //修改库存了
- Goods buyGoods = new Goods();
- buyGoods.setId( goodsId);
- buyGoods.setAmount(nums);
- goodsDao.updateGoods(buyGoods);
- System.out.println("=====buy方法的完成====");
- }
- public void setSaleDao(SaleDao saleDao) {
- this.saleDao = saleDao;
- }
- public void setGoodsDao(GoodsDao goodsDao) {
- this.goodsDao = goodsDao;
- }
- }
-
测试入下:
-
正常查询:
-
查询一个不存在的一条数据
-
购买的数量超过库中的数据
-
此时我们在查询一条正常的
-
2.使用 AspectJ 的 AOP 配置管理事务(掌握)
适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
-
Step1:maven 依赖 pom.xml
- 新加入 aspectj 的依赖坐标
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-aspectsartifactId>
- <version>5.2.5.RELEASEversion>
- dependency>
-
Step:在容器中添加事务管理器
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="myDataSource" />
- bean>
-
修改spring配置文件(重点)
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- https://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
- <context:property-placeholder location="classpath:jdbc.properties" />
- <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
- init-method="init" destroy-method="close">
- <property name="url" value="${jdbc.url}" />
- <property name="username" value="${jdbc.username}"/>
- <property name="password" value="${jdbc.passwd}" />
- <property name="maxActive" value="${jdbc.max}" />
- bean>
- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="myDataSource" />
- <property name="configLocation" value="classpath:mybatis.xml" />
- bean>
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
- <property name="basePackage" value="com.bjpowernode.dao"/>
- bean>
- <bean id="buyService" class="com.bjpowernode.service.impl.BuyGoodsServiceImpl">
- <property name="goodsDao" ref="goodsDao" />
- <property name="saleDao" ref="saleDao" />
- bean>
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="myDataSource" />
- bean>
- <tx:advice id="myAdvice" transaction-manager="transactionManager">
- <tx:attributes>
- <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
- rollback-for="java.lang.NullPointerException,com.bjpowernode.excep.NotEnoughException"/>
- <tx:method name="add*" propagation="REQUIRES_NEW" />
- <tx:method name="modify*" />
- <tx:method name="remove*" />
- <tx:method name="*" propagation="SUPPORTS" read-only="true" />
- tx:attributes>
- tx:advice>
- <aop:config>
- <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
- <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
- aop:config>
- beans>
-
此时达到的效果和上面spring的注解完成的效果是一样的,这里就不测试了。