第5章 Spring的事务管理
5.1 Spring事务管理概述
5.11 事务管理的核心接口
在Spring的所有jar包中,有一个名spring-tx-4.3.6RELEAS的jar包,是提供事务管理的依赖包。在该包的org.springframework.transaction包中,三个核心接口文件分别是PlatformTransactionManager、TransactionDefinition和TransactionStatus
1.PlatfromTransactionManager
PlatfromTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。有3个事务操作的方法:
TransactionStatus getTransaction( TransactionDefinition definition ):用于获取事务状态信息
void commit( TransactionStatus status ):提交事务
void rollback( Transaction status ):回滚事务
PlatformTransactionManager接口只是代表事务管理的接口,并不知道底层是如何管理事务的,具体如何管理事务则由它的实现类来完成。该接口常见的几个实现类如下:
(1)、用于配置JDBC数据源的事务管理器:
org.springframework.jdbc.datasource.DataSourceTransactionManager
(2)、用于配置Hibernate的事务管理器:
org.springframework.orm.hibernate4.HibernateTransactionManager
(3)、用于配置全局事务管理器:
org.springframework.transaction.jta.JtaTransactionManager
当底层采用不同的持久层技术时,系统只需使用不同的实现类。
2.TransactionDefinition
事务定义(描述)的对象,该对象中定义了事务规则,并提供获取事务相关信息的方法,具体如下:
String getName():获取事务对象名称
int getlsolationLevel:获取事务的隔离级别
int getPropagationBehavior():获取事务的传播行为
int getTimeout():获取事务的超时时间
boolean isReadOnly():获取事务的是否只读
传播行为:指在同一个方法中,不同操作前后所使用的事务。传播行为有很多种:
传播行为可以控制是否需要创建事务以及如何创建事务,查询不影响数据的改变,不需要事务管理;插入、更新和删除需要事务管理。如果没有指定事务的传播行为,Spring默认传播行为是REQUIRED(required)
3.TransactionStatus
TransactionStatus接口是事务的状态,它描述了某一时间点上事务的状态信息,该接口有6个方法:
void flush():刷新事务
boolean hasSavepoint():获取是否存在保存点
boolean isCompleted():获取事务是否完成
boolean isNewTransaction():获取是否是新事物
boolean isRollbackOnly():获取是否回滚
void setRollbackOnly():设置事务回滚
5.12 事务管理的方式:
传统的编程式事务管理:通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚
声明式事务管理:通过AOP技术实现的事务管理,主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将管理“切面”代码织入到业务目标类中。
5.2 声明式事务管理
5.21 基于XML方式的声明式事务
tx命名空间下提供了<tx:advice>元素来配置事务的通知(增强处理)。当使用<tx:advice>元素配置了事务的增强处理后,就可以通过编写AOP配置,让Spring自动对目标生成代理。
配置<tx:advice>元素时,通常需要指定id和transaction-manager属性。transaction-manager属性用于指定事务管理器。除此之外,还需要配置一个<tx:attributes>子元素,该子元素可以通过配置多个<tx:method>子元素来配置执行事务的细节。
转账方法编写:
/** * 转账 * 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); }
XML文件:
<?xml version="1.0" encoding="UTF-8"?> <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 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 1.配置数据源 --> <bean id="dataSource222" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--数据库驱动 --> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <!--连接数据库的url --> <property name="url" value="jdbc:mysql://localhost/spring" /> <!--连接数据库的用户名 --> <property name="username" value="root" /> <!--连接数据库的密码 --> <property name="password" value="****" /> </bean> <!-- 2.配置JDBC模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!-- 默认必须使用数据源 --> <property name="dataSource" ref="dataSource222" /> </bean> <!--3.定义id为accountDao的Bean --> <bean id="accountDao" class="com.itheima.jdbc.AccountDaoImpl"> <!-- 将jdbcTemplate注入到AccountDao实例中 --> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean> <!-- 4.事务管理器,依赖于数据源 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource222" /> </bean> <!-- 5.编写通知:对事务进行增强(通知),需要编写对切入点和具体执行事务细节 --> <tx:advice id="txAdvice123" 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.itheima.jdbc.*.*(..))" id="txPointCut111" /> <!-- 切面:将切入点与通知整合 --> <aop:advisor advice-ref="txAdvice123" pointcut-ref="txPointCut111" /> </aop:config> </beans>
测试类:获取实例,调用方法,常规操作
@Test///基于XML方式的声明式事务 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("转账成功!"); }
5.22 基于Annotation(注解)方式的声明式事务
两个套路
1.在Spring容器中注册事务注解驱动:
<tx:annotation-driven transaction-manager="transactionManager"/>
XML文件:
<?xml version="1.0" encoding="UTF-8"?> <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 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 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/spring" /> <!--连接数据库的用户名 --> <property name="username" value="root" /> <!--连接数据库的密码 --> <property name="password" value="17251104238" /> </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.itheima.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:annotation-driven transaction-manager="transactionManager"/> </beans>
2.在方法transfer()前加注解:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
方法:
@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); }