Spring 05 -SpringAOP事务管理
SpringAOP事务管理
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)