Spring的事务管理
事务管理的核心接口
在Spring的所有JAR包中,包含一个名为spring-tx-4.3.6.RELEASE的JAR包,该包就是Spring提供的用于事务管理的依赖包。在该JAR包的org.springframework.transaction包中,我们可以找到3个接口文件
1、PlatformTransactionManager
PlatformTransactionManager接口是Spring平台事务管理器,主要用于事务管理。该接口中提供了3个事务操作的方法。
1)TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息。
2)void commit(TransactionStatus status):用于提交事务。
3)void rollback(TransactionStatus status):用于回滚事务。
getTransaction方法会根据TransactionDefinition 参数返回一个TransactionStatus 对象,TransactionStatus 对象就表示一个事务,他被关联在当前执行的线程上。
PlatformTransactionManager接口只是代表事务管理的接口,它并不知道底层是如何管理事务的,它只需要事务提供上面的3个方法,但具体如何管理事务则由它的实现类来完成。
PlatformTransactionManager接口有许多不同的实现类,常见的几个实现类
1)org.springframework.jdbc.datasource.DataSourceTransactionManager:用于配置JDBC数据源的事务管理器。
2)org.springframework.orm.hibernate4.HibernateTransactionManager:用于配置Hibernate的事务管理器。
3)org.springframework.transaction.jta.JtaTransactionManager:同于配置全局事务管理器。
当底层采用不同的持久层技术时,系统只需使用不同的Platform TransactionManager实现类即可。
2、TransactionDefinition
TransactionDefinition接口是事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法
1)String getName():获取事务对象名称。
2)int getIsolationLevel():获取事务的隔离级别。
3)int getPropagationBahavior():获取事务的传播行为。
4)int getTimeout():获取事务的超时事件。
5)boolean isReadOnly():获取事务是否只读。
上述方法中,事务的传播行为是指在同一个方法中,不同操作前后使用的事务。传播行为有很多种。
属性名称 | 值 | 描述 |
---|---|---|
PROPAGATION_REQUIRED | REQUIRED | 表示当前方法必须运行在一个事务环境当中,如果当前方法已处于事务环境中,则可以直接使用该方法;否则会开启一个新事物后执行该方法。 |
PROPAGATION_SUPPORTS | SUPPORTS | 如果当前方法处于事务环境中,则使用当前事务,否则不使用事务。 |
PROPAGATION_MANDATORY | MANDATORY | 表示调用该方法的线程必须处于当前事务环境中,否则将抛异常。 |
PROPAGATION_REQUIRES_NEW | REQUIRES_NEW | 要求方法在新事务环境中执行,如果当前方法已在事务环境中,则先暂停当前事务,在启动新的事务后执行该方法;如果当前方法不在事务环境中,则会启功一个新的事务后执行方法。 |
PROPAGATION_NOT_SUPPORTED | NOT_SUPPORTED | 不支持当前事务,总是以非事务状态执行。如果调用该方法的线程处于事务环境中,则先暂停事务,然后执行该方法。 |
PROPAGATION_NEVER | NEVER | 不支持当前事务,如果调用该方法的线程处于事务环境中,将抛出异常。 |
PROPGATION_NESTED | NESTED | 即使当前执行的方法处于事务环境中,依然会启动一个新的事务,并且方法在嵌套的事务里执行;即使当前执行的方法不在事务环境中,也会启动一个新事物,然后执行该方法。 |
在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务,通常情况下,数据的查询不会影响原数据的改变,所以不需要进行事务管理,而对应数据的插入、更新和删除操作,必须进行事务管理。如果没有指定事务的传播行为,Spring默认传播行为是REQUIRED。
3、TransactionStatus
TransactionStatus接口是事务的状态,它描述了某一时间点上事务的状态信息。该接口中包含6个方法
1)void flush():刷新事务。
2)boolean hasSavepoint():获取是否存在保存点。
3)boolean isCompleted():获取事务是否完成。
4)boolean isNewTransaction():获取是否是新事物。
5)boolean isRollbackOnly():获取是否回滚。
6)void setRollbackOnly():设置事务回滚。
事务管理的方式
Spring中的事务管理分为两种方式:一种是传统的编程式管理,另一种是声明式事务管理。
1、编程式事务管理:是通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
2、声明式事务管理:是通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个"切面"代码单独编写,然后通过AOP技术将事务管理的"切面"代码织入到业务目标类中。
声明式事务管理最大的优点在于开发者无需通过编程的方式来管理事务,只需在配置文件中进行相关的事务规则声明,就可以将事务规则应用到业务逻辑中。这使得开发人员可以更加专注于核心都推荐使用声明式事务管理。
声明式事务管理
基于XML方式得声明式事务
基于XML方式得声明式事务管理是通过在配置文件中配置事务规则得相关声明来实现的。Spring2.0以后,提供了tx命名空间来配置事务,tx命名空间下提供了<tx:advice>
元素来配置事务的通知(增强处理)。当使用<tx:advice>
元素配置了事务的增强处理后,就可以通过编写的AOP配置,让Spring自动对目标生成代理。
配置<tx:advice>
元素时,通常需要指定id和transaction-manager属性,其中id属性是配置文件中的唯一标识,transaction-manager属性用于指定事务管理器。除此之外,还需要配置一个<tx:attributes>
子元素,该子元素可通过配置多个<tx:method>
子元素来配置执行事务的细节。
配置<tx:advice>
元素的重点是配置<tx:method>
子元素
属性名称 | 描述 |
---|---|
name | 该属性为必须属性,它指定了与事务属性相关的方法名。其属性值支持使用通配符,如* 、get* 、handle* 、*Oeder 等 |
propagation | 用于指定事务的传播行为,默认为REQUIRED |
isolation | 该属性用于指定事务的隔离级别,其属性值可以为DEFAULT、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE,其默认值为DEFAULT |
read-only | 该属性用于指定事务是否只读,其默认值false |
timeout | 该属性用于指定事务超时的时间,其默认值为-1,即永不超时 |
rollback-for | 该属性用于指定事务回滚的异常类,在指定多个异常类时,异常类之间以英文逗号分隔 |
no-rollback-for | 该属性用于指定不触发事务回滚的异常类,在指定多个异常类,异常类之间以英文逗号分隔 |
1)创建一个chapter05的web项目
2)将chapter04的代码复制到chapter05项目的src目录下,并在AccountDao接口中,创建一个转账方式transfer()
public void transfer(String outUser,String inUser,Double money)
3)在其实现类AccountDaoImpl中实现transfer()方法
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);
}
在上述代码中,使用了两个update()方法对account表中的数据执行收款和汇款的更新操作。在两个操作之间,添加一行代码"i=1/0"来模拟系统运行时的突发性问题。
4)修改配置文件applicationContext.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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountDao" class="jdbc.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* jdbc.*.*(..))" id="txPointCut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
5)测试类TransactionTest
package jdbc;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TransactionTest {
@Test
public void xmlText(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
AccountDao accountDao=(AccountDao)applicationContext.getBean("accountDao");
accountDao.transfer("Jack", "Rose", 100.0);
System.out.println("转账成功!");
}
}
账户没有改变。
基于Annotation方式的声明式事务
1、Spring容器中注册事务注解驱动
<tx:annotation-driven transaction-manager="transactionManager"/>
2、在需要使用事务的Spring Bean类或者Bean类的方法上添加注解@Transactional。如果注解添加在Bean类上,则表示事务的设置对整个Bean有效;如果添加在方法上,则对该方法有效。
参数名称 | 描述 |
---|---|
value | 用于指定需要使用的事务管理器,默认为"",其别名为transactionManager |
transactionManager | 指定事务的限定符,可用于确定目标事务管理器 |
isolation | 用于指定事务的隔离级别,默认为Isolation.DEFAULT |
noRollbackFor | 用于指定遇到特定异常时强制不回滚事务 |
noRollbackForClassName | 用于指定遇到特定多个异常时强制不回滚事务。属性可以指定多个异常类名 |
propagation | 用于指定事务的传播行为,默认为Propagation.REQUIRED |
read-only | 用于指定事务是否只读,默认为false |
rollbackFor | 用于指定特定异常时强制回滚事务 |
rollbackForClassName | 用于指定特定多个异常时强制回滚事务。其属性值可以指定多个异常类名 |
timeout | 用于指定事务的超时时长,默认为TransactionDefinition.TIMEOUT_DEFAULT |
@Transactional注解与<tx:method>
元素中的事务属性基本是对应的,并且其含义也基本相同。
1)applicationContext-annotation.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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountDao" class="jdbc.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2)在AccountDaoImpl类的transfer()方法上添加事务注解
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
3)修改测试类的spring配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext-annotation.xml");