1.事务管理的核心接口
1.1 PlatformTransactionManager管理事务
该接口主要用于管理事务,提供了3个操作事务的方法,具体如下
方法名 | 描述 |
TransactionStatus getTransaction(TransactionDefinition definition) | 用于获取事务状态信息 |
void commit(TransactionStatus status) | 用于提交事务 |
void rollback(TransactionStatus status) | 用于回滚事务 |
1.2 TransactionDefinition
TransactionDefinition 接口是事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法
方法名 | 描述 |
String getName() | 获取事务对象名称 |
int getlsolationLevel() | 获取事务的隔离级别 |
int getPropagationBehavior() | 获取事务的传播行为 |
int getTimeout | 获取事务的超时时间 |
boolean isReadOnly() | 获取事务是否只读 |
事务的传播行为是指在同一个方法中,不同操作前后所使用的事务,传播行为有很多种,如图所示:
属性名称 | 值 | 描述 |
PROPAGATION_REQUIRED | REQUIRED | 表示当前方法必须运行在一个事务环境当中,如果当前方法已处于事务环境中,则可以直接使用该方法;否则会开启一个新事务后执行该方法 |
PROPAGATION_SUPPORTS | SUPPORTS | 如果当前方法处于事务环境中,则使用当前事务,否则不使用事务 |
PROPAGATION_MANDATORY | MANDATORY | 表示调用该方法的线程必须处于当前事务环境中,否则将抛异常 |
PROPAGATION_REQUIRES_NEW | REQUIRES_NEW | 要求方法在新的事务环境中执行.如果当前方法已在事务环境中,则先暂停当前事务,在启动新的事务后执行该方法;如果当前方法不在事务环境中,则会启动一个新的事务后执行方法 |
PROPAGATION_NOT_SUPPORTED | NOT_SUPPORTED | 不支持当前事务,总是以非事务状态执行.如果调用该方法的线程处于事务环境中,则先暂停当前事务,然后执行该方法 |
PROPAGATION_NEVER | NEVER | 不支持当前事务,总是以非事务状态执行.如果调用该方法的线程处于事务环境中,则抛异常 |
PROPAGATION_NESTED | NESTED | 即使当前执行的方法处于事务环境中,依然会启动一个新的事务,并且方法在嵌套的事务里执行;即使当前执行的方法不在事务环境中,也会启动一个新事务,然后执行该方法. |
1.3 TransactionStatus
TransactionStatus接口是事务的状态,它描述了某一时间点上事务的状态信息,该接口中包含6个方法
方法名 | 描述 |
void flush() | 刷新事务 |
boolean hassavepoint() | 获取是否存在保存点 |
boolean is Completed() | 获取事务是否完成 |
boolean is NewTransaction() | 获取是否是新事务 |
boolean isRollbackOnly() | 获取是否回滚 |
void setRollbackOnly() | 设置事务回滚 |
2. 事务管理的方式
- 编程式事务管理
通过编写代码实现的事务管理,包括定义事务的开始,正常执行后的事务提交和异常时的事务回滚
- 声明式事务管理
通过AOP技术实现的事务管理,将事务管理作为一个"切面"代码单独编写,然后通过AOP技术将事务管理的"切面"代码织入到业务目标类中
2.1 声明式事务管理
通过两种方式来实现,一种是XML,一种是基于Annotation的方式
2.1.2 基于XML方式的声明式事务
属性名称 | 描述 |
name | 该属性为必选属性,它指定了与事务属性相关的方法名.其属性值支持使用通配符,如*、get*、handle*、*Order |
propagation | 用于指定事务的传播行为,默认值是REQUIRED |
isolation | 该属性用于指定事务的隔离级别,其属性值可以为DEFAULT(默认值)、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE |
read-only | 该属性用于指定事务是否只读,其默认值为false |
timeout | 该属性用于指定事务的超时时间,其默认值为-1,即永不超时 |
rollback-for | 该属性用于指定触发事务回滚的异常类,在指定多个异常类时,用逗号分隔 |
no-rollback-for | 该属性用于指定不触发事务回滚的异常类,在指定多个异常类时,用逗号分隔 |
案例:模拟银行转账
(1)导入AOP需要的JAR包
(2)AccountDao接口中,创建转账方法transfer()
(3)AccountDaoImpl
/*
转账
inUser:收款人
outUser:汇款人
money:金额
*/
public void transfer(String outUser,String inUser,Double money){
//收款时,收款用户的余额=现有余额+所汇金额
this.jdbcTemplate.update("update account set balance=balance+?"+"where username=?",money,inUser);
//模拟系统运行时的突发性问题
int i=1/0;
//汇款时,汇款用户的余额=现有金额-所汇金额
this.jdbcTemplate.update("update account set balance=balance-?"
+"where username=?",money,outUser);
}
(4)修改applicationContext.xml,添加命名空间并编写事务管理的相关配置代码
<!--1.配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--数据库驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url-->
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<!--连接数据库的用户名-->
<property name="username" value="root"/>
<!--连接数据库的密码-->
<property name="password" value="root"/>
</bean>
<!--2.配置JDBC模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--默认必须使用数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--3.定义id为accountDao的Bean-->
<bean id="accountDao" class="com.itcast.jdbc.AccountDaoImpl">
<!--将jdbcTemplate注入到AccountDao实例中-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!--4.事务管理器,依赖于数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.dataSource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--5.编写通知:对事务进行增强(通知),需要编写对切入点和具体执行事务细节-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--name:*表示任意方法名称-->
<tx:method name="*" propagation="REQUIRED"
isolation="DEFAULT" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--6.编写aop,让spring自动对目标生成代理,需要使用AspectJ的表达式-->
<aop:config>
<!--切入点-->
<aop:pointcut expression="execution(* com.itcast.jdbc.*.*(..))"
id="txPointCut"/>
<!--切面:将切入点与通知整合-->
<aop:advisor advice-ref="tx:Advice" pointcut-ref="txPointCut"/>
</aop:config>
(5)测试类TransactionTest
//测试类
public class TransactionTest{
@Test
public void xmlTest(){
//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//获取AccountDao实例
AccountDao accountDao=(AccountDao )applicationContext.getBean("accountDao");
//调用实例中的转账方法
accountDao.transfer("Jack","Rose",100.0);
System.out.println("转账成功");
}
}
2.1.3 基于Annotation方式的声明式事务
两个步骤:
- 在Spring容器中注册事务注解驱动,其代码如下
<tx:annotation-driven transaction-manager="transactionManager"/>
- 在需要使用事务的SpringBean类或者Bean类的方法上添加注解@Transactional,如果将注解添加在Bean类上,则表示事务的设置对整个Bean类的所有方法都起作用;如果将注解添加在Bean类中的某个方法上,则表示事务的设置只对该方法有效
参数名称 | 描述 |
value | 用于指定需要使用的事务管理器,默认为"",别名为transactionManager |
transactionManager | 指定事务的限定符值,可用于确定目标事务管理器,匹配特定的限定值(或者Bean的name值),默认为"",其别名为value |
isolation | 用于指定事务的隔离级别,默认为Isolation.DEFAULT(即底层事务的隔离级别) |
noRollbackFor | 用于指定遇到特定异常时强制不回滚事务 |
noRollbackForClassName | 用于指定遇到特定的多个异常时强制不回滚事务.其属性值可以指定多个异常类名 |
propagation | 用于指定事务的传播行为,默认为Propagation.REQUIRED |
read-only | 用于指定事务是否只读,默认为false |
rollbackFor | 用于指定遇到特定异常时强制回滚事务 |
rollbackForClassName | 用于指定遇到特定的多个异常时强制回滚事务.其属性值可以指定多个异常类名 |
timeout | 用于指定事务的超时时长,默认为TransactionDefinition.TIMEOUT_DEFAULT(即底层事务系统的默认时间) |
(1)创建一个Spring的配置文件applicationContext-annotation.xml,在该文件中声明事务管理器等配置信息
<!--1.配置数据源-->
<bean id="dataSource"
<!--1.配置数据源-->
<bean id="datasource"class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<!--数据库驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url-->
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<!--连接数据库的用户名-->
<property name="username" value="root"/>
<!--连接数据库的密码-->
<property name="password" value="root"/>
</bean>
<!--2.配置JDBC模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--默认必须使用数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--3.定义id为accountDao的Bean-->
<bean id="accountDao" class="com.itcast.jdbc.AccountDaoImpl">
<!--将jdbcTemplate注入到accountDao实例中-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!--4.事务管理器,依赖于数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.core.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">
</bean>
<!--5.注册事务管理器驱动-->
<tx:annotation-driven transaction-Manager="transactionManager"/>
(2)在AccountDaoImpl类的transfer()方法上添加事务注解
/*
转账
inUser:收款人
outUser:汇款人
money:金额
*/
@Transactional(propagation=Propagation.REQUIRED,
isolation=Isolation.DEFAULT,readOnly=false)
public void transfer(String outUser,String inUser,Double money){
//收款时,收款用户的余额=现有余额+所汇金额
this.jdbcTemplate.update("update account set balance=balance+?"+"where username=?",money,inUser);
//模拟系统运行时的突发性问题
int i=1/0;
//汇款时,汇款用户的余额=现有金额-所汇金额
this.jdbcTemplate.update("update account set balance=balance-?"
+"where username=?",money,outUser);
}
(3)创建测试方法annotationTest()
//测试类
@Test
public void annotationTest{
//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext-annotation.xml");
//获取AccountDao实例
AccountDao accountDao=(AccountDao )applicationContext.getBean("accountDao");
//调用实例中的转账方法
accountDao.transfer("Jack","Rose",100.0);
System.out.println("转账成功");
}