Spring Framework系列文章
Spring Framework Data Access | 03 Spring 事务控制
1. Spring 事务控制
1.1 为什么要事务控制
问题:在同一个方法中可能有多个对于数据库进行访问更新的语句,这些语句之间彼此关联
public void ServiceMethod(){
//数据库存储的数据对应的类
DataClass c1 = new DataClass();
DataClass c2 = new DataClass();
//对于这两个对象进行一系列关联的操作,比如增加减少一个相同的数
//Dao指持久层对象
Dao.update(c1);
//假设这里的某些代码报错
Dao.update(c2);
}
当在两个更新语句之间出现错误时
若不进行事务控制,对c1的修改提交了而c2的修改因为错误的出现并未进行提交
进行事务控制保证对c1和c2的操作要么都成功修改数据库要么都不修改
事务控制实际上就是将这一系列操作当做一个不可拆分的单元,保证操作要么全部成功要么全部失败
1.2 事务控制的实现
(到目前为止都是使用JDBC进行数据库的访问)
-
线程绑定:将数据库的访问创建的连接绑定到线程上,同方法中的访问都使用一个连接(不可拆分)
-
提交回滚:commit( )和rollback( )是事务控制中最基本的两个操作,有这些才能进行事务控制
具体:将自动提交改为手动提交,所有语句成功执行才提交否则全部回滚(全部成功或全部失败)
1.3 Spring 事务控制的实现
Spring的事物控制位于业务层
Spring的事务控制使用事务控制器进行也是基于AOP实现的
Tips:事务管理器实际上就是专门用于事务控制的通知类
Spring 事务控制的三个接口
-
PlatFromTransactionManager:事务管理器,提供了常用事务管理操作( commit 和 rollback )
DataSourceTransactionManager:接口实现类在使用Spring JDBC进行数据持久化时使用
HibernateTransactionManager:接口实现类在使用Hibernate进行数据持久化时使用 -
TransactionDefinition:定义事务的属性(名称、隔离级别、传播行为、只读、超时时间)
- TransactionStatus:定义事务的运行状态
+ 事务隔离级别
(只是为了展示隔离级别的属性,具体结合数据库)
事务隔离级别 Isolation:指定当遇到事务提交并发访问时的处理方式
事务隔离级别属性:
- DEFAULT:默认,以下四个属性之一有数据库本身决定
- READ_UNCOMMITTED:可以读取未提交的数据
- READ_COMMITTED:只能读取提交的数据
- REPEATABLE_READ:读取其他事务提交修改后的数据
- SERIALIZABLE:读取其他事务提交添加后的数据
+ 事务传播行为
(只是为了展示传播行为的属性,具体结合数据库)
事务传播行为 Propagation:指定当调用到多个业务逻辑时事务的规定方式,保证事务的一致性
事务传播行为属性:
-
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中
-
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
-
MANDATORY:使用当前的事务,如果当前没有事务,抛出异常
-
NEVER:以非事务方式执行,如果当前存在事务,抛出异常
-
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
-
NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起
-
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 的操作
Tips:对于是否需要有事务的问题
- 查询操作事务可有可无 SUPPORTS
- 增加、删除、更新操作必须要有事务支持 REQUIRED
2. Spring声明式事务控制
2.1 基于XML的配置
2.1.1 前期准备
- 导入aop和tx的XML头文件约束
- 配置事务管理器,将事务管理器存入IoC容器
2.1.2 配置事务通知
使用 tx:advice 标签配置事务管理的通知
属性:
-
id:指定事务管理通知的唯一标识
-
transaction-manager:指定事务管理器的Bean对象
2.1.3 配置事务属性
在 tx:advice 中使用 tx:attributes 标签的子标签 tx:method 配置事务的属性
属性:
-
name:指定核心业务方法即需要进行事务管理的方法
-
read-only:指定事务是否为只读事务,默认为读写( false )
-
isolation:指定事务的隔离级别,默认使用数据库的默认隔离级别
-
propagation:指定事务的传播行为
-
timeout:指定事务的超时时间,默认永不超时
-
rollback-for:指定一个异常,当产生该异常时,事务回滚,其他异常事务不回滚
-
no-rollback-for:指定一个异常,当产生该异常时,事务不回滚,其他异常事务回滚
2.1.4 配置切入点表达式
配置切入点和AOP的方式相同
2.1.5 配置事务通知和切入点之间的关系
在 aop:config 中使用 aop:advisor 标签配置管理器和切入点的关系
属性:
-
advice-ref:指定事务通知的引用
-
point-ref:指定切入点表达式的引用
Tips:可以理解为在配置切面
2.1.6 配置示例
业务层代码
//业务层
package org.example;
public class ServiceClass{
//一般会使用Spring JDBC中的JdbcTemplate
public void method1(..){ };
public T method2(..){ };
//......
}
配置文件.xml代码
<!--XML头文件约束:aop、tx-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="...">
<property name="..." value="..."></property>
<!--省略了数据源的设置-->
</bean>
<!--配置事务通知-->
<tx:advice id="txManager" transaction-manager="transactionManager">
<!--配置事务属性-->
<tx:attributes>
<tx:method name="*" propagation=".." read-only=".."></tx:method>
</tx:attributes>
</tx:advice>
<!--配置事务通知和切入点之间的关系-->
<aop:config>
<aop:pointcut id="pt" expression="execution(* org.example.AdvisorClass.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txManager" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
2.2 基于注解的配置
2.2.1 前期准备
- 导入context、aop和tx的XML头文件约束
- 配置事务管理器,将事务管理器存入IoC容器
2.2.2 配置事务管理
使用 @Transaction 注解在相应的类或方法出增加事务管理
属性:和XML配置事务属性中除name以外的属性相同
Tips:相当于将XML的所有配置集成在一步完成
Tips:当有多个不同属性的事务时每个事务都要使用@Transaction单独配置以此属性
2.2.3 开启注解声明式事务控制的支持
在XML文件中使用 tx:annotation-driven 标签开启对注解声明式事务控制的支持
属性:transaction-manager:指定事务管理器的Bean对象
2.2.4 配置示例
业务层代码
//业务层
package org.example;
@Service("serviceClass")
//针对类中的所有方法进行配置
@Transactional(propagation=Propagation.REQUIRED,read-only=false)
public class ServiceClass{
//针对类中的某一个方法进行配置,这里只针对method1
@Transactional(propagation=Propagation.SUPPORTS,read-only=true)
public void method1(..);
public T method2(..);
//....
}
配置文件.xml代码
<!--XML头文件约束:aop、tx、context-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="...">
<property name="..." value="..."></property>
<!--省略了数据源的设置-->
</bean>
<!--扫描注解-->
<context:component-scan base-package="org.example"></context:component-scan>
<!--开启注解声明式事务控制的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
2.3 基于纯注解的配置
2.3.1 开启注解声明式事务控制的支持
在纯注解配置类上使用 @EnableTransactionManagement 注解开启对注解声明式事务控制的支持
@Configrution
@ComponentScan(basepackages="org.example")
@EnableTransactionManagment
public class ConfigruationClass{
@Bean(name="transactionManager")
public TransactionManager getTransactionManager(DataSource dataSource){
return new TransactionManager(dataSource);
}
@Bean(name="dataSource")
public DataSource getDataSource(..){
//省略了数据源的设置driverClass、url等
return new dataSource();
}
}
2.4 选择哪种配置?
- 对于简单的配置使用注解较为方便,比如单个事务使用,全是自己编写的类
- 对于多个事务的复杂配置要写多个注解使用XML更为方便
3. Spring 编程式事务控制
使用TransactionTemplate类中的execute( )方法
- 在execute( )参数中编写接口TransactionCallback的匿名内部类
- 将业务层方法本来所要执行的操作编写到匿名内部类中实现事务控制
public void method1(..) {
transactionTemplate.execute(new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) {
//业务层method1本身要执行的代码
}
});
}
不常用:对于每一个方法都要编写一个execute( )方法和匿名内部类,代码重复度增大