文章目录
- 事务回顾
- 事务介绍
- 事务并发问题(隔离性导致)
- 事务隔离级别
- Spring框架事务管理相关接口
- Spring框架事务管理的分类
- 编程式事务管理(了解)
- 声明式事务管理(重点)
- 事务管理之XML方式
- 业务层
- 持久层
- 单元测试代码
- 配置事务管理的AOP
- 事务管理之混合方式
- 事务管理之基于AspectJ的纯注解方式
事务回顾
事务介绍
事务:指的是逻辑上一组操作,组成这个事务的各个执行单元,要么一起成功,要么一起失败
事务的特性(ACID):
- 原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
- 一致性(Consistency)
一致性是指事务必须使数据库一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还是5000,这就是事务的一致性
- 隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离
- 持久性
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作
事务并发问题(隔离性导致)
在事务的并发操作中可能会出现一些问题
- 脏读:一个事务读取到另一个事务未提交的数据
- 不可重复读:一个事务因读取到另一个事务已提交的数据。导致对同一条记录读取两次以上的结果不一致
- 幻读:一个事务因读取到另一个事务已提交的数据。导致对同一张表读取两次以上的结果不一致。insert、delete操作
事务隔离级别
为了避免上面出现的几种情况,在标注SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同
- 四种隔离级别:
现在来看看MySQL数据库为我们提供的四种隔离级别(由低到高):
- Read uncommitted(读未提交):最低级别,任何情况都无法保证
- Read committed(读已提交):可避免脏读的发生
- Repeatable read(可重复读):可避免脏读、不可重复读的发生
- Serializable(串行化):可避免脏读、不可重复读、幻读的发生
-
默认隔离级别
大多数数据库的默认隔离级别是Read Committed(RC),比如Oracle、DB2等。
MySQL数据库的默认隔离级别是Repeatable Read(RR) -
注意事项
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大
对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Comitted。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制
Spring框架事务管理相关接口
Spring并不直接管理事务,而是提供了事务管理接口是PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了
1.PlatformTransactionManager接口 – 平台事务管理器(真正管理事务的类)。该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类
2.TransactionDefinition接口 – 事务定义信息(事务的隔离级别,传播行为,超时,只读)
3.TransactionStatus接口 --事务的状态(是否新事务、是否已提交、是否有保存点、是否回滚)
4.总结:上述对象之间的关系:平台事务管理器真正管理事务对象 根据事务定义的信息TransactionDefinition进行事务管理,在管理事务中产生一些状态 将状态记录到TransactionStatus中
5.PlatformTransactionManager接口中实现类和常用的方法
1.接口的实现类
如果使用的Spring的JDBC模板或者MyBatis(IBatis)框架,需要选择DataSourceTransactionManager实现类
如果使用的是Hibernate的框架,需要选择HibernateTransactionManager实现类2.该接口的常用方法
void commit(TransactionStatus status)
TransactionStatus getTransaction(TransactionDefinition definition)
void rollback(TransactionStatus status)
6.TransactionDefinition
1.事务隔离级别的常量
static int ISOLATION_DEFAULT --采用数据库的默认隔离级别
static int ISOLATION_READ_UNCOMMITTED
static int ISOLATION_READ_COMMITTED
static int ISOLATION_REPEATABLE_READ
static int ISOLATION_SERIALIZABLE2.事务的传播行为常量(不用设置,使用默认值)
先解释什么是事务的传播行为:解决的是业务层之间的方法调用
PROPAGATION_REQUIRED(默认值) – A中有事务,使用A中的事务。如果没有,B就会开启一个新的事务,将A包含进来。(保证A,B在同一个事务中),默认值
PROPAGATION_SUPPORTS – A中有事务,使用A中的事务。如果A中没有事务,那么B也不使用事务。
PROPAGATION_MANDATOTY – A中有事务,使用A中的事务。如果A没有事务,抛出异常
PROPAGATION_REQUIRES_NEW – A中有事务,将A中的事务挂起。B创建一个新的事务(保证A,B没有在一个事务中)
PROPAGATION_NOT_SUPPORTED – A中有事务,将A中的事务挂起
PROPAGATION_NEVER --A中有事务,抛出异常
PROPAGATION_NESTED --嵌套事务。当A执行之后,就会在这个位置设置一个保存点。如果B没有问题,执行通过。如果B出现异常,运行客户根据需求回滚(选择回滚到保存点或者最初始状态)
Spring框架事务管理的分类
1.Spring的编程式事务管理(不推荐使用)
通过手动编写代码的方式完成事务的管理(不推荐)
2.Spring的声明式事务管理(底层采用AOP的技术)
通过一段配置的方式完成事务的管理
编程式事务管理(了解)
说明:Spring为了简化事务管理的代码:提供了模板类TransactionTemplate,所以手动编程的方式来管理事务,只需要使用该模板类即可
手动编程方式的具体步骤如下:
1.步骤一:配置一个事务管理器,Spring使用PlatformTransactionManager接口来管理事务,所以需要使用到他的实现类
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.步骤二:配置事务管理的模板
<!--配置事务管理的模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
3.步骤三:在需要进行事务管理的类中,注入事务管理的模板
<bean id="accountService" class="com.spring.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="transactionTemplate" ref="transactionTemplate" />
</bean>
4.步骤四:在业务层使用模板管理事务
//注入事务模板对象
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void pay(final String out, final String in, final double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult(){
protected void doInTransactionWithoutResult(TransactionStatus status) {
//扣钱
accountDao.outMoney(out,money);
int a = 10/0;
//加钱
accountDao.inMoney(in,money);
}
});
}
声明式事务管理(重点)
声明式事务管理又分成三种方式
- 基于AspectJ的XML方式(重点掌握)
- 基于AspectJ的注解+XML混用方式(重点掌握)
- 基于AspectJ的纯注解方式(重点掌握)
事务管理之XML方式
准备转账环境:
业务层
AccountService
AccountServiceImpl
public void transfer(String in, String out, double money) {
dao.outMoney(out,money);
//异常代码
System.out.println(1/0);
dao.inMoney(in,money);
}
持久层
AccountDao
AccountDaoImpl
spring配置
<!--配置数据源连接池(C3P0) -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql:///web01" />
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<bean class="cn.spring.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<context:component-scan base-package="cn.spring.service"></context:component-scan>
单元测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(location={"classpath:applicationContext-tx.xml"})
public class TransactionTest {
@Autowired
private AccountService service;
@Test
public void test01() {
service.transfer("a","b",100);
}
}
配置事务管理的AOP
- 平台事务管理器:DataSourceTransactionManager
<!-- 配置平台事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
- 事务通知
<tx:advice id=“” transaction-manager=“” />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务相关属性 -->
<tx:attributes>
<!-- 对方法级别设置 事务的隔离级别、事务的传播行为-->
<!-- 设置了默认的隔离级别和传播行为-->
<tx:method name="transfer" />
<!-- 提交订单-->
<tx:method name="submitOrder" />
<!-- 添加 -->
<tx:method name="add*" />
<tx:method name="insert*" />
<!-- 删除 -->
<tx:method name="delete*" />
<tx:method name="remove*" />
<!-- 修改 -->
<tx:method name="update*" />
<tx:method name="modify*" />
<!-- 查询 -->
<tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" />
<tx:method name="query*" read-only="true" />
</tx:attributes>
</tx:advice>
- AOP配置:
<aop:config>
<aop:advisor advice-ref="" pointcut=""/>
</aop:config>
<!--配置AOP-->
<aop:config>
<!--切入点:所有的业务层实现类中方法-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn..service.*.*(..))"/>
</aop:config>
事务管理之混合方式
- service类上或方法上加注解:
类上加@Transactional:表示该类中所有的方法都被事务管理
方法上加@Transactional:表示只有该方法被事务管理
- 开启事务注解:
<!-- 配置平台事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
事务管理之基于AspectJ的纯注解方式
@EnableTransactionManagement