关于Spring的事务管理

Spring支持两种方式的事务管理

  • 编程式事务管理
  • 声明式事务管理

编程式事务管理的两种实现方法

  1. 使用PlatformTransactionManager接口的事物管理器
  2. 使用TransactionTemplate实现

声明式事务管理的四种实现方法

  1. 基于 TransactionInterceptor 的声明式事务: Spring声明式事务的基础,通常也不建议使用这种方式,但是与前面一样,了解这种方式对理解 Spring 声明式事务有很大作用。
  2. 基于TransactionProxyFactoryBean 的声明式事务: 第一种方式的改进版本,简化的配置文件的书写,这是 Spring早期推荐的声明式事务管理方式,但是在 Spring 2.0 中已经不推荐了。
  3. 基于< tx> 和< aop>命名空间的声明式事务管理: 目前推荐的方式,其最大特点是与 Spring AOP 结合紧密,可以充分利用切点表达式的强大支持,使得管理事务更加灵活。
  4. 基于@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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值