> 文章列表 > Spring 05 -SpringAOP事务管理

Spring 05 -SpringAOP事务管理

Spring 05 -SpringAOP事务管理

SpringAOP事务管理

  • 1 事务
    • 1.1 事务概念
    • 1.2 事务的特性
    • 1.3 并发问题
    • 1.4 隔离级别
    • 1.5 隔离级别的特性
  • 2 自定义事务管理
    • 2.1 事务管理类
    • 2.2 事务管理工具类
    • 2.3 DBUtils实现的Dao操作
    • 2.4 业务层转账操作
    • 2.5 SpringAOP事务管理配置
    • 2.6 注解式配置
  • 3 声明式事务管理
    • 3.1 导入依赖
    • 3.2 配置事务管理器
    • 3.3 配置事务通知(增强)
      • 3.3.1 属性说明
    • 3.4 配置事务切面
    • 3.5 基于注解配置

1 事务

1.1 事务概念

事务是一个原子操作。是一个最小执行单元。可以由一个或多个SQL语句组成,在同一个事务当中,所有的SQL语句都成功执行时,整个事务成功,有一个SQL语句执行失败,整个事务都执行失败。

1.2 事务的特性

  • Atomicity(原子性)

一个事务中的事务操作,不可再分,要一起成功要么一起失败

  • Consistency(一致性)

一个或多个事务的操作,在操作前后数据是一致的

  • Isolation(隔离性)

一个事务的操作不能影响到另外一个事务

  • Durability(持久性)

一旦事务条件,将会永久保存到数据库中

1.3 并发问题

事务并发时的安全问题

问题 描述
脏读 一个事务读取到另一个事务还未提交的数据。大于等于 read-commited 可防止
不可重复读 一个事务内多次读取一行数据的相同内容,其结果不一致。大于等于 repeatable-read 可防止
幻读 一个事务内多次读取一张表中的相同内容,其结果不一致。serialized-read 可防止

1.4 隔离级别

名称 描述
default (默认值)(采用数据库的默认的设置) (建议)
read-uncommited 读未提交
read-commited 读提交 (Oracle数据库默认的隔离级别)
repeatable-read 可重复读 (MySQL数据库默认的隔离级别)
serialized-read 序列化读

隔离级别由低到高为:read-uncommited < read-commited < repeatable-read < serialized-read

1.5 隔离级别的特性

  • 安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。

  • 并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。

2 自定义事务管理

技术:Spring+DBUtils、AOP

1)增强类 (负责对方法进行增强,开启事务、提交事务、回滚事务、关闭资源)

2)配置切面(增强+切入点)

注意:要保证这个操作中的Connetion是同一个

2.1 事务管理类

public class TXAdvice {@AutowiredTXUtils txUtils;public Object around(ProceedingJoinPoint joinPoint){try {//开启事务txUtils.start();//调用目标对象的方法Object obj = joinPoint.proceed();//提交事务txUtils.commit();return obj;} catch (Throwable throwable) {throwable.printStackTrace();//回滚事务txUtils.rollback();}finally {//释放资源txUtils.close();}return null;}
}

2.2 事务管理工具类

//事务工具类
@Component
public class TXUtils {@AutowiredDataSource dataSource;ThreadLocal<Connection> tl = new ThreadLocal<>();public Connection getConnection(){try {Connection conn = tl.get();if(conn == null){conn = dataSource.getConnection();//保存ThreadLocal中tl.set(conn);}return conn;} catch (SQLException throwables) {throwables.printStackTrace();}return null;}public void start(){Connection connection = getConnection();try {connection.setAutoCommit(false);} catch (SQLException throwables) {throwables.printStackTrace();}}public void commit(){Connection connection = getConnection();try {connection.commit();} catch (SQLException throwables) {throwables.printStackTrace();}}public void rollback(){Connection connection = getConnection();try {connection.rollback();} catch (SQLException throwables) {throwables.printStackTrace();}}public void close(){Connection connection = getConnection();try {connection.close();//在ThreadLocal移除Connection对象tl.remove();} catch (SQLException throwables) {throwables.printStackTrace();}}
}

2.3 DBUtils实现的Dao操作

@Repository
public class AccountDaoImpl implements AccountDao {@Autowiredprivate QueryRunner qr;@AutowiredTXUtils txUtils;@Overridepublic Account selectAccount(String name) throws SQLException {return qr.query(txUtils.getConnection(),"select * from tb_account where name = ?",new BeanHandler<Account>(Account.class),name);}@Overridepublic int updateAccount(String name, double money) throws SQLException {String sql = "update tb_account set money = money + ? where name = ?";return qr.update(txUtils.getConnection(),sql,money,name);}
}

2.4 业务层转账操作

@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic String zhuanzhang(String fromName, String toName, double money) throws SQLException {//1、判断用户是否存在Account account = accountDao.selectAccount(fromName);if(account == null){return "用户不存在";}//2、判断余额是否足够if(account.getMoney() < money){return "余额不足";}//3、判断对方账户是否存在Account toAccount = accountDao.selectAccount(toName);if(toAccount == null){return "对方用户不存在";}//4、我方扣钱accountDao.updateAccount(fromName,-money);System.out.println(10/0);//5、敌方加钱accountDao.updateAccount(toName,money);return "转账成功";}
}

2.5 SpringAOP事务管理配置

<bean id="advice" class="com.ying.advice.TXAdvice"></bean><!-- AOP配置-->
<aop:config><!-- AOP切面配置--><aop:aspect ref="advice"><!-- AOP切入点配置--><aop:pointcut id="pc" expression="execution(* com.ying.service..*.*(..))"/><!-- AOP通知配置--><aop:around method="around" pointcut-ref="pc"/></aop:aspect>
</aop:config>

2.6 注解式配置

<!-- 开启springAOP的注解扫描 -->
<aop:aspectj-autoproxy/>
@Component
@Aspect
public class TXAdvice { //通知增强@AutowiredTXUtils txUtils;@Pointcut("execution(* com.ying.service..*.*(..))")public void pc(){}@Around("pc()")   public Object around(ProceedingJoinPoint joinPoint){try {//开启事务txUtils.start();//调用目标对象的方法Object obj = joinPoint.proceed();//提交事务txUtils.commit();return obj;} catch (Throwable throwable) {throwable.printStackTrace();//回滚事务txUtils.rollback();}finally {//释放资源txUtils.close();}return null;}    
}

3 声明式事务管理

3.1 导入依赖

<!-- Spring声明式事务管理依赖 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.3.6</version>
</dependency>
<!-- Spring声明式事务织入依赖 -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version>
</dependency>

3.2 配置事务管理器

 <!-- 配置Spring事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 注入数据源信息--><property name="dataSource" ref="dataSource"/>
</bean>

注意:DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一个DataSource,否则事务控制失败!!!

3.3 配置事务通知(增强)

<!-- 配置Spring事务的增强类-->
<tx:advice id="txAdvice" transaction-manager="transactionManager" ><!-- 配置Spring事务的增强类的信息--><tx:attributes><!--name:Spring:事务增强类增强的方法isolation:事务的隔离级别propagation:事务传播行为REQUIRED(以事务的方式运行,如有事务,加入事务,如无事务,则创建事务)   更新SUPPORTS(有事务则以事务方式运行,没有事务以非事务方式运行)            查询read-only:是否只读     查询true  更新falsetimeout:超时时间(默认为-1表示永久等待)   设置单位为‘秒’rollback-for:设置哪些异常回滚no-rollback-for:设置哪些异常不回滚增强方法的配置技巧通配符匹配get*select*find*query 其他所有方法--><tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/><tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/><tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/><tx:method name="query*" isolation="DEFAULT"propagation="SUPPORTS" read-only="true" timeout="-1"/><tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/></tx:attributes>
</tx:advice>

3.3.1 属性说明

isolation 隔离级别

名称 描述
default (默认值)(采用数据库的默认的设置) (建议)
read-uncommited 读未提交
read-commited 读提交 (Oracle数据库默认的隔离级别)
repeatable-read 可重复读 (MySQL数据库默认的隔离级别)
serialized-read 序列化读

propagation 事务传播行为

名称 作用
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

当涉及到事务嵌套(Service调用Service)时,可以设置:

  • SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询)

  • REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。 (默认值)(适合增删改)

readonly 读写性

  • true:只读,可提高查询效率。(适合查询)
  • false:可读可写。 (默认值)(适合增删改)

timeout事务超时时间

当前事务所需操作的数据被其他事务占用,则等待。

  • 1000:自定义等待时间1000(秒)。
  • -1:由数据库指定等待时间,默认值。(建议)

rollbackFor 配置回滚的异常

noRollbackFor 配置不回滚的异常

3.4 配置事务切面

<!-- 配置Spring切面-->
<aop:config><!-- 配置Spring切入点--><aop:pointcut id="pc" expression="execution(* com.ying.service..*.*(..))"/><!-- 配置Spring增强类--><aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>

3.5 基于注解配置

 <!-- Spring声明式事务基于注解的配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 注入数据源信息--><property name="dataSource" ref="dataSource"/>
</bean><!--  开启Spring事务的包扫描  -->
<tx:annotation-driven transaction-manager="transactionManager"/>

在service层的方法或者类上添加注解

  • 注解加在类上表示所有的方法都使用事务管理
  • 注解加在方法上仅表示当前方法使用事务管理
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false,timeout = -1)