在声明式的事务处理中,要配置一个切面,其中就用到了propagation,表示打算对这些方法怎么使用事务,是用还是不用,其中propagation有七种配置,REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。默认是REQUIRED。
Spring中七种Propagation类的事务属性详解:
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
Spring框架的 事务有4种隔离级别isolation:
隔离级别 名称 含义 脏读 不可重复读 幻读
读未提交 ISOLATION_READ_UNCOMMITTED T1读取T2未提交的东西 V V V
读已提交 ISOLATION_READ_COMMITTED mysql默认,事务提交后可读 X V V
可重复读 ISOLATION_REPEATABLE_READ 针对update操作;会出现幻读现象,幻读针对的是insert操作 X X V
串行化 ISOLATION_SERIALIZABLE 解决了脏读、不可重复读和幻读,但是效率比较低 X X X
1. 第一种:小型的项目事务处理及配置
<!--使用spring事务处理-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接数据库,告诉数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解驱动,告诉spring使用注解驱动,生成代理对象
transaction-manager:事务管理器id
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
// 可以加载类上,这样这个类所有的方法就都是有事务的,不过一般不这么做
// @Transactional
@Service("buyGoodService")
public class BuyGoodServiceImpl implements BuyGoodService {
@Autowired
private SaleDao saleDao;
@Autowired
private GoodsDao goodsDao;
/**
* propagation = Propagation.REQUIRED 传播行为,如果存在一个事务,则支持当前事务。如果没有事务则开启。
* isolation = Isolation.DEFAULT 事务隔离级别
* readOnly = false 只读
* rollbackFor 表示发生指定的异常一定回滚
* 处理逻辑是:
* 1、spring会先检异常是不是在rollbackFor属性值中,如果在,不管什么异常都会执行回滚
* 2、如果不在rollbackFor中,spring会判断是不是RuntimeException运行时异常,是的话也会回滚
*
*/
/* @Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class,NotEnoughException.class
}
)*/
// 因为上面的配置信息都是默认的,所以直接写一个注解就行
// 抛出运行时异常
/*
* 因为加了事务,顺序执行到if语句时,会抛出异常,所以就会进行事务回滚rollbackFor
* 数据就不会提交到数据库中
* */
@Transactional
@Override
public void buyGoods(Integer id,Integer snum){
// 记录信息,向sale表添加记录
// 目前这样写是不对的,因为还需要判断goods的数量是否充足才能添加
Sale sale = new Sale(id,snum);
saleDao.insertSale(sale);
// 判断goods的id是否存在,和数量书否充足
Goods goods = goodsDao.selectGood(id);
if(goods == null){
throw new NullPointerException("编号为"+id+"的商品不存在");
}else if(goods.getGamount() < snum){
throw new NotEnoughException("编号为"+id+"的商品数量不够,"+"购买数量:"+snum+",原剩余数量:"+goods.getGamount());
}
Goods buyGoods = new Goods(id,snum);
// 更新goods的数量
goodsDao.updateGoods(buyGoods);
}
}
第二种:大型项目事务配置
无需在业务中配置,事务与业务代码分离
<!-- 导入以来Aop框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--
大型项目:声明业务方法的事务属性(隔离级别,传播行为,超时时间)
-->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<!--配置事务属性-->
<tx:attributes>
<!--
tx:method:给事务配置属性,可以有多个method,给多个方法配置事务
name="buyGoods" 需要事务的方法名
propagation:隔离级别
isolation:传播行为
rollback-for:指定的异常,全限定名称,发生异常一定回滚
-->
<tx:method name="buyGoods" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,com.wsy.excep.NotEnoughException"/>
<!--多个开头一样的方法配置 开头重复部分*-->
<tx:method name="add*" propagation="REQUIRES_NEW"/>
<tx:method name="delete*"/>
<!--select方法设置成只读最好-->
<tx:method name="select*" read-only="true"/>
<!--除了以上配置好的以外的-->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置Aop-->
<aop:config>
<!--配置切入点表达式,说明哪些包中的哪些类要使用事务
id:自定义名称
expression:切入点表达式
execution(* *..service..*.*(..)):这样写就能保证无论service在哪个包下,
service包以及子包的所有类都会加入事务
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增强器,关联上面的advice和pointcut
advice-ref:上面<tx:advice>的id
pointcut-ref:pointcut的id
-->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="servicePt"/>
</aop:config>
execution(* *..service..*.*(..))
这样写就能保证无论service在哪个包下,service包以及子包的所有类都会加入事务