一、Spring事务管理概述
1、事务管理的核心接口
2、事务管理的方式
Spring中的事务管理分为两种方式:一种是传统的编程式事务管理,另一种是声明式事务管理。
编程式事务管理:是通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
声明式事务管理:是通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码织入到业务目标类中。
二、声明式事务管理
1)基于XML方式的声明式事务
基于XML方式的声明式事务管理是通过在配置文件中配置事务规则的相关声明来实现的。
Spring2.0以后,提供了tx命名空间来配置事务,tx命名空间下提供了<tx: advice>元素来配置事务的通知(增强处理)。
<!-- 5. 编写通知:对事物进行通知,需要编写对切入点和具体执行事务细节 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
</tx:attributes>
</tx:advice>
配置<tx:advice>
时,要指定id属性(配置文件中的唯一标识)和transaction-manager属性(用于指定事务管理器)。<tx:advice>
中有<tx:attributes>
子元素。子元素中有<tx:method>
元素,用于配置执行事务的细节。 <tx:method>
元素的属性描述如下:
属性 | 是否必须 | 默认值 | 描述 |
---|---|---|---|
name | 是 | 与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法 | 如:‘* ’、’get* ’、’handle* ’、’* Order ’等等。 |
propagation | 否 | REQUIRED | 事务传播行为 |
isolation | 否 | DEFAULT | 事务隔离级别 |
timeout | 否 | -1 | 事务超时的时间(以秒为单位) ,如果为-1,则说明事务超时时间由底层的事务系统所决定 |
read-only | 否 | false | 事务是否只读 |
rollback-for | 否 | 所有运行期异常回滚 | 触发事务回滚的Excepiton,用异常名称的片段进行匹配,可以设置多个,用逗号分开,如Exception1,Exception2 |
norollback-for | 否 | 所有检查型异常不回滚 | 不触发事务回滚的Excepiton,用异常名称的片段进行匹配,可以设置多个,用逗号分开,如Exception1,Exception2 |
案例:通过xml方式实现Spring的声明式事务管理 模拟一个赠送积分的项目,体现事务管理
导入相关JAR包
在原有数据库中添加jf属性类型设为int(11),所有用户默认1000积分。
在User类中添加jf及其setter、getter方法
private Integer jf;
public Integer getJf() {
return jf;
}
public void setJf(Integer jf) {
this.jf = jf;
}
在UserDao接口中添加transfer()方法,三个参数
public void transfer(String outUser,String inUser,Integer jf);
在UserDaoImpl类中实现并完善方法this关键字用于当前类
//赠送积分
public void transfer(String outUser, String inUser, Integer jf) {
//接收积分
this.jdbcTemplate.update("update user set jf=jf+? where username=?",jf,inUser);
//模拟系统运行中突发性问题
int i=1/0;
//送出积分
this.jdbcTemplate.update("update user setjf=jf-? where username=?",jf,outUser);
}
修改applicationContext.xml
1、配置数据源
2、配置jdbc模板
3、定义id为userDao的bean
4、事务管理器,依赖于数据源
5、编写通知:对事物进行通知,需要编写对切入点和具体执行事务细节
6、编写aop,让spring自动对目标生成代理,需要使用AspectJ
<?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/aop
http://www.springframework.org/schema/aop/spring-aop-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">
<!-- 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/db_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为userDao的bean -->
<bean id="userDao" class="com.ssm.jdbc.UserDaoImpl">
<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>
<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.ssm.jdbc.*.*(..))" id="txPointCut" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
</beans>
定义了id为transactionManager的事务管理器,下面用编写的通知来声明事务,最后用声明aop的方式让Spring自动生成代理。
编写测试类
package com.ssm.jdbc;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TransactionTest {
@Test
public void XMLTest(){
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao=(UserDao) app.getBean("userDao");
userDao.transfer("admin1", "HQX", 100);
System.out.println("赠送积分成功:");
}
}
Junit控制台中报出了“/ y zero”的算术异常信息。在执行赠送积分操作后,查看 user表中的数据,即zhangsan和lisi的积分没有发生变化,这说明 Spring中的事务管理配置已经生效。
2)基于Annotation方式的声明式事务
Spring的声明式事务管理还可以通过 Annotation(注解)的方式来实现。这种方式的使用非常简单,开发者只需做两件事情:
(1)在 Spring容器中注册事务注解驱动;
<tx:annotation-driven transaction-manager="transactionManager" />
(2)在需要使用事务的 Spring Bean类或者Bean类的方法上添加注解@Transactional。
@Transactional注解中常用参数说明
参数名称 | 功能描述 |
---|---|
value | 用于需要使用的事务管理器,默认为“”,其别名为transactionManager |
propagation | propagation用于指定事务的传播行为,默认值为 REQUIRED |
isolation | isolation用于指定事务的隔离规则,默认值为DEFAULT |
readOnly | 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称:@Transactional(rollbackForClassName=“RuntimeException”)指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”}) |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName=“RuntimeException”)指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”,“Exception”}) |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
src下新建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-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-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">
<!-- 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/db_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为userDao的bean -->
<bean id="userDao" class="com.ssm.jdbc.UserDaoImpl">
<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>
添加测试类
@Test
public void AnnocationTest(){
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao=(UserDao) app.getBean("userDao");
userDao.transfer("admin1", "HQX", 100);
System.out.println("赠送积分成功:");
}
}