Spring事务

spring提供一种处理事务的统一模型,能使用统一的步骤,完成多种不同数据库访问技术的事务处理。

spring管理事务的三个方面

1、事务管理器
事务管理器是一个接口和它的众多实现类,由事务管理器对象完成事务的提交和回滚。
实现类:spring将每一种数据库访问技术对应的事务处理类都创建好了
mybatis访问数据库——-DateSourceTransactionManager
hibernate访问数据库———HibernateTransactionManager
使用方法:告诉spring你使用的那种数据库的访问方式

1
<bean id="  " class="  ...DateSourceTransaction"/>

2、事务的类型
(1)事务的隔离级别

(2)事务的超时时间
表示一个方法的最长执行时间,如果方法执行超过了该时间,事务就回滚
(3)常用的事务传播行为

  • REQUIRED(Spring默认):内外层方法共用外层方法的事务,要么都成功,要么都不成功。
  • REQUIRES_NEW:内外层方法相互独立,互不影响。
  • NESTED:内层方法不会影响外层,外层回滚时内层也会回滚。

注: 使用代理对象调用当前类中的另外一个事务方法,不然事务会失效

1
2
1 ServiceA proxy =(ServiceA)AopContext.currentProxy();
2 proxy.b();

3、spring提交事务,回滚事务的时机
(1)业务方法执行成功,没有异常抛出,spring会在方法指向后提交事务
(2)业务方法抛出运行时异常,spring执行回滚
运行时异常定义:RuntimeException和它的子类都是运行时异常,NullPointException,NumberFormatException
(3)业务方法抛出非运行时异常,受查异常时,提交事务。
受查异常:写代码时必须处理的异常,IOException,SQLException

事务应用——购买商品项目

实现购买商品,模拟用户下订单,向订单中添加销售记录,从商品表中减少库存。

注解方式

1、在spring配置文件添加事务管理器和事务注解驱动

1
2
3
4
5
6
7
8
<!-- 声明事务管理器   -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 连接的数据库,指定数据源 -->
<property name="dataSource" ref="myDataSource"/>
</bean>

<!-- 开启事务注解驱动 告诉spring使用注解管理事务,创建代理对象-->
<tx:annotation-driven transaction-manager="transactionManager"/>

2、在需要使用事务的方法上添加Transactional注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class BuyGoodsServiceImpl implements BuyGoodsService {
private SaleDao saleDao;
private GoodsDao goodsDao;

public void setSaleDao(SaleDao saleDao) {
this.saleDao = saleDao;
}

public void setGoodsDao(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}

/*@Transactional(
propagation = Propagation.REQUIRED,// 传播行为(默认)
isolation = Isolation.DEFAULT,// 隔离级别(默认)
readOnly = false,
rollbackFor = { // 发生指定异常时回滚
RuntimeException.class
}

)*/
@Transactional
@Override
public void buy(Integer gid, Integer nums) {
System.out.println("buy开始");
// 记录销售信息,向sale表中添加记录
saleDao.insertSale(new Sale(null, gid, nums));

Goods goods = goodsDao.selectGoodsById(gid);
if (goods == null) {
throw new RuntimeException("商品不存在!");
}else if(goods.getAmount()<nums){
throw new RuntimeException("商品不足");
}

// 更新库存
goodsDao.updateGoods(new Goods(gid,null,nums,null));

System.out.println("buy结束");
}
}

使用aspectj完成事务配置

1、添加aspectj依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>

2、声明事务管理器

1
2
3
4
5
<!-- 声明事务管理器   -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 连接的数据库,指定数据源 -->
<property name="dataSource" ref="myDataSource"/>
</bean>

3、声明业务方法及事务属性

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--  声明业务方法及事务属性(隔离级别、传播行为、超时时间)  -->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes><!-- name 方法名称 可使用通配符-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.RuntimeException"/>
<!--修改方法 -->
<tx:method name="update*"/>
<!--删除方法 -->
<tx:method name="delete*"/>
<!--查询方法 -->
<tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>

4、配置aop

1
2
3
4
5
6
7
8
9
10
11
<!--配置aop  -->
<aop:config>
<!--配置切入点表达式:指定哪些包中的类需要使用事务
id:切入点表达式的名称,唯一值
expression:切入点表达式,指定哪些类需要使用事务,aspectj会自动创建代理对象-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"></aop:pointcut>
<!--配置增强器 关联advice和pointcut
advice-ref 通知,tx:advice的id值
pointcut-ref 切入点表达式aop:pointcut的id值 -->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>