Spring支持两种方式的事务管理
- 编程式事务管理
- 声明式事务管理
编程式事务管理的两种实现方法
- 使用PlatformTransactionManager接口的事物管理器
- 使用TransactionTemplate实现
声明式事务管理的四种实现方法
- 基于 TransactionInterceptor 的声明式事务: Spring声明式事务的基础,通常也不建议使用这种方式,但是与前面一样,了解这种方式对理解 Spring 声明式事务有很大作用。
- 基于TransactionProxyFactoryBean 的声明式事务: 第一种方式的改进版本,简化的配置文件的书写,这是 Spring早期推荐的声明式事务管理方式,但是在 Spring 2.0 中已经不推荐了。
- 基于< tx> 和< aop>命名空间的声明式事务管理: 目前推荐的方式,其最大特点是与 Spring AOP 结合紧密,可以充分利用切点表达式的强大支持,使得管理事务更加灵活。
- 基于@Transactional 的全注解方式: 将声明式事务管理简化到了极致。开发人员只需在配置文件中加上一行启用相关后处理 Bean的配置,然后在需要实施事务管理的方法或者类上使用@Transactional 指定事务规则即可实现事务管理,而且功能也不必其他方式逊色。
实现案例
由于目前在声明式编程管理上绝大多数都是使用基于< tx>和< aop>命名空间的声明事务式管理的方式和基于@Transactional注解的方式,所以对于声明式我只挑这两个来做案例,而对于编程式事务管理我则选择使用TransactionTemplate来实现。
首先,我的案例数据库都是使用Mysql。你需要先创建数据库表
CREATE TABLE `account` (
`username` varchar(99) DEFAULT NULL,
`salary` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
注意:你所创建数据库表所使用的数据库引擎应该是InnoDB,而不是MyISAM。这是因为MyISAM引擎并不支持事务处理也不支持外来键,而InnoDB支持。
(1)使用TransactionTemplate来实现编程式事务管理
OrderDao.java(DAO层)
package cn.itcast.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class OrderDao {
//注入JdbcTemplate模板对象
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//减钱的方法(小王)
public void reduceMoney(){
String sql = "UPDATE `account` SET salary=salary-? WHERE username=?";
jdbcTemplate.update(sql, 1000, "小王");
}
//加钱的方法(小马)
public void addMoney(){
String sql = "UPDATE `account` SET salary=salary+? WHERE username=?";
jdbcTemplate.update(sql, 1000, "小马");
}
}
OrderService.java(Service层)
package cn.itcast.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import cn.itcast.dao.OrderDao;
public class OrderService {
//注入Dao层对象
private OrderDao orderDao;
public OrderDao getOrderDao() {
return orderDao;
}
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
//注入TransactionTemplate对象
private TransactionTemplate transactionTemplate;
public TransactionTemplate getTransactionTemplate() {
return transactionTemplate;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
//调用dao层的方法
//业务逻辑,写转账业务
public void accountMoney(){
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
Object result = null;
try{
orderDao.addMoney();
int i = 10/0; //出现异常,下面已经捕获并回滚
orderDao.reduceMoney();
System.out.println("转让成功!");
}catch(Exception e){
status.setRollbackOnly();
result = false;
System.out.println("转让失败!");
}
return result;
}
});
}
}
配置文件:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性值 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/student"></property>
<property name="user" value="root"></property>
<property name="password" value="199802"></property>
</bean>
<!-- 编程式事务管理 -->
<!-- 配置事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!-- 注入真正进行事务管理的事务管理器,name必须为transactionManager -->
<property name="transactionManager" ref="dataSourceTransactionManager"></property>
</bean>
<!-- JDBC模板对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 对象生成以及以及属性注入 -->
<bean id="orderDao" class="cn.itcast.dao.OrderDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="orderService" class="cn.itcast.service.OrderService">
<property name="orderDao" ref="orderDao"></property>
<!-- 注入事务管理模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
</beans>
OrderTest.java(测试类)
package cn.itcast.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.itcast.service.OrderService;
public class OrderTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderService orderService = (OrderService) context.getBean("orderService");
orderService.accountMoney();
}
}
(2)基于AspectJ的声明式事务管理
Order1Service.java(Service层)
package cn.itcast.service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import cn.itcast.dao.OrderDao;
public class Order1Service {
private OrderDao orderDao;
public OrderDao getOrderDao() {
return orderDao;
}
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void accountMoney(){
try{
orderDao.addMoney();
int i=10/0; //出现异常,
orderDao.reduceMoney();
System.out.println("执行成功!");
}catch(Exception e){
System.out.println("执行失败!");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动回滚事务
// throw new RuntimeException(); //必须抛出异常,才能让aop捕获并进行事务回滚
}
}
}
注意:方法中执行的try-catch块之后需要进行代码手动回滚或者把捕获的异常再次抛出去,这是因为aop事务管理机制只有在异常未被捕获时才能进行回滚。
Spring AOP事务处理机制:
默认情况下,只能捕获unchecked异常,但经过配置后也同样可以捕获checked异常。
对于异常捕获,aop事务机制只能捕获未处理的异常。也就是说当被拦截的方法抛出异常后,不能对该异常进行任何捕获处理。只有这样aop事务机制才能捕获到该异常,最终再根据配置的属性决定是否将此事务标记为回滚。
配置文件:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性值 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/student"></property>
<property name="user" value="root"></property>
<property name="password" value="199802"></property>
</bean>
<!-- 第一步:配置事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 第二步:配置事务增强(通知) -->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<!-- 事务操作 -->
<tx:attributes>
<!-- 根据方法规则设置事务操作 -->
<!--
propagation: 匹配开头为account的所有方法
isolation:事务传播行为
read-only:事务隔离级别
read-only:事务是否只读
rollback-for:发生哪些异常事务回滚
timeout:事务过期时间
-->
<tx:method name="accountMoney"
propagation="REQUIRED"
isolation="DEFAULT"
read-only="false"
rollback-for="RuntimeException"
timeout="-1" />
</tx:attributes>
</tx:advice>
<!-- 第三步:配置切面(通知器),把事务通知和切入点注入切面(通知器) -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* cn.itcast.service.Order1Service.*(..))" id="pointcut"/>
<!-- 将事务通知和切点注入切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
<!-- 对象生成以及属性注入 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="orderDao" class="cn.itcast.dao.OrderDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="order1Service" class="cn.itcast.service.Order1Service">
<property name="orderDao" ref="orderDao"></property>
</bean>
OrderTest1.java(测试类)
package cn.itcast.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.itcast.service.Order1Service;
import cn.itcast.service.OrderService;
public class OrderTest1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
Order1Service order1Service = (Order1Service) context.getBean("order1Service");
order1Service.accountMoney();
}
}
(3)基于@transactional注解方式的声明式事务管理
Order2Service.java(Service层)
package cn.itcast.service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import cn.itcast.dao.OrderDao;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false, timeout = -1)
public class Order2Service {
private OrderDao orderDao;
public OrderDao getOrderDao() {
return orderDao;
}
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void accountMoney(){
try{
orderDao.addMoney();
int i = 10/0;
orderDao.reduceMoney();
System.out.println("转让成功!");
}catch(Exception e){
System.out.println("转让失败!");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动回滚
}
}
}
配置文件:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- 配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注入属性值 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/student"></property>
<property name="user" value="root"></property>
<property name="password" value="199802"></property>
</bean>
<!-- 第一步:配置事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 第二步:开启事务注解 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" />
<!-- 生成对象和注入属性 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="orderDao" class="cn.itcast.dao.OrderDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="order2Service" class="cn.itcast.service.Order2Service">
<property name="orderDao" ref="orderDao"></property>
</bean>
</beans>
注意:使用注解方式时,需要在配置文件中手动开启事务注解。
OrderTest2.java(测试类)
package cn.itcast.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.itcast.service.Order1Service;
import cn.itcast.service.Order2Service;
import cn.itcast.service.OrderService;
public class OrderTest2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
Order2Service order2Service = (Order2Service) context.getBean("order2Service");
order2Service.accountMoney();
}
}
PS:本文章只用于个人学习理解和巩固~
【如有理解错误,欢迎纠正!】
学习资料文章:
https://www.jianshu.com/p/4183c3fbb7b4
https://blog.csdn.net/yipanbo/article/details/46048413?utm_source=blogxgwz1
https://www.jianshu.com/p/f5fc14bde8a0
https://www.cnblogs.com/kevingrace/p/5685355.html