12-Spring事务深入理解和使用
in 004-源码分析 with 0 comment

12-Spring事务深入理解和使用

in 004-源码分析 with 0 comment

1 事务基本特性

1.1 原子性(Atomicity)

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

1.2 一致性(Consistency)

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态

拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性

1.3 隔离性(Isolation)

隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离

即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

1.4 持久性(Durability)

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

2 事务控制分类

2.1 编程式事务控制

自己手动控制事务,就叫做编程式事务控制。

// 设置手动控制事务
Conn.setAutoCommite(false); 
// 开启一个事务
Session.beginTransaction();

2.2 声明式事务控制

spring提供了对事务的管理, 这个就叫声明式事务管理

Spring提供了对事务控制的实现。用户如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。这个实现了对事务控制的最大程度的解耦,spring声明式事务管理,核心实现就是基于Aop

Spring声明式事务管理器类

3 手写Spring事务框架

3.1 编程事务实现

使用编程事务实现,手动事务 begin、commit、rollback

@Component
public class TransactionUtils {
	@Autowired
	private DataSourceTransactionManager dataSourceTransactionManager;
	// 开启事务
	public TransactionStatus begin() {
		TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
		return transaction;
	}
	// 提交事务
	public void commit(TransactionStatus transactionStatus) {
		dataSourceTransactionManager.commit(transactionStatus);
	}
	// 回滚事务
	public void rollback(TransactionStatus transactionStatus) {
		dataSourceTransactionManager.rollback(transactionStatus);
	}
}
@Service
public class UserService {
	@Autowired
	private UserDao userDao;
	@Autowired
	private TransactionUtils transactionUtils;
	public void add() {
		TransactionStatus transactionStatus = null;
		try {
			transactionStatus = transactionUtils.begin();
			userDao.add("wangmazi", 27);
			int i = 1 / 0;
			System.out.println("我是add方法");
			userDao.add("zhangsan", 16);
			transactionUtils.commit(transactionStatus);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (transactionStatus != null) {
				transactionStatus.rollbackToSavepoint(transactionStatus);
			}
		}
	}
}
@Component
@Aspect
public class AopTransaction {
	@Autowired
	private TransactionUtils transactionUtils;
	// // 异常通知
	@AfterThrowing("execution(* com.itmayiedu.service.UserService.add(..))")
	public void afterThrowing() {
		System.out.println("程序已经回滚");
		// 获取程序当前事务 进行回滚
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	}
	// 环绕通知
	@Around("execution(* com.itmayiedu.service.UserService.add(..))")
	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("开启事务");
		TransactionStatus begin = transactionUtils.begin();
		proceedingJoinPoint.proceed();
		transactionUtils.commit(begin);
		System.out.println("提交事务");
	}
}

事务手动回滚代码
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

3.2 声明事务实现

管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	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.xsd
     	 http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.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">
	<!-- 开启注解 -->
	<context:component-scan base-package="com.itmayiedu"></context:component-scan>
	<!-- 1. 数据源对象: 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/test"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
	</bean>
	<!-- 2. JdbcTemplate工具类实例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 配置事物 -->
	<bean id="dataSourceTransactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 开启注解事物 -->
	<tx:annotation-driven transaction-manager="dataSourceTransactionManager" />
</beans>

使用

@Transactional
public void add() {
    userDao.add("wangmazi", 27);
    int i = 1 / 0;
    System.out.println("我是add方法");
    userDao.add("zhangsan", 16);
}

3.3 手写Spring注解版本事务

3.3.1 实现自定义注解

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:

说明了Annotation所修饰的对象范围

TYPE:类, 接口 (包括注释类型), 或 枚举 声明
FIELD:字段声明(包括枚举常量)
METHOD:方法声明(Method declaration)
PARAMETER:正式的参数声明
CONSTRUCTOR:构造函数声明
LOCAL_VARIABLE:局部变量声明
PACKAGE:包声明
TYPE_PARAMETER:类型参数声明
TYPE_USE:使用的类型

表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

SOURCE:注释只在源代码级别保留,编译时被忽略
CLASS:注释将被编译器在类文件中记录,但在运行时不需要JVM保留。这是默认的行为
RUNTIME:注释将被编译器记录在类文件中,在运行时保留VM,因此可以反读

表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分

是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的

3.3.2 自定义事务注解

//编程事务(需要手动begin 手动回滚  手都提交)
@Component()
@Scope("prototype") // 设置成原型解决线程安全
public class TransactionUtils {
	private TransactionStatus transactionStatus;
	// 获取事务源
	@Autowired
	private DataSourceTransactionManager dataSourceTransactionManager;
	// 开启事务
	public TransactionStatus begin() {
		transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
		return transactionStatus;
	}
	// 提交事务
	public void commit(TransactionStatus transaction) {
		dataSourceTransactionManager.commit(transaction);
	}
	// 回滚事务
	public void rollback() {
		System.out.println("rollback");
		dataSourceTransactionManager.rollback(transactionStatus);
	}
}
注解类
@Autowired
	private TransactionUtils transactionUtils;
	@AfterThrowing("execution(* com.itmayiedu.service.*.*.*(..))")
	public void afterThrowing() throws NoSuchMethodException, SecurityException {
		// isRollback(proceedingJoinPoint);
		System.out.println("程序发生异常");
		// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		// TransactionStatus currentTransactionStatus =
		// TransactionAspectSupport.currentTransactionStatus();
		// System.out.println("currentTransactionStatus:" +
		// currentTransactionStatus);
		transactionUtils.rollback();
	}
	// // 环绕通知 在方法之前和之后处理事情
	@Around("execution(* com.itmayiedu.service.*.*.*(..))")
	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		// 调用方法之前执行
		TransactionStatus transactionStatus = begin(proceedingJoinPoint);
		proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出异常不会执行后面代码
		// 调用方法之后执行
		commit(transactionStatus);
	}
	public TransactionStatus begin(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
		// // 判断是否有自定义事务注解
		ExtTransaction declaredAnnotation = getExtTransaction(pjp);
		if (declaredAnnotation == null) {
			return null;
		}
		// 如果有自定义事务注解,开启事务
		System.out.println("开启事务");
		TransactionStatus transactionStatu = transactionUtils.begin();
		return transactionStatu;
	}
	public void commit(TransactionStatus transactionStatu) {
		if (transactionStatu != null) {
			// 提交事务
			System.out.println("提交事务");
			transactionUtils.commit(transactionStatu);
		}
	}
	public ExtTransaction getExtTransaction(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
		// 获取方法名称
		String methodName = pjp.getSignature().getName();
		// 获取目标对象
		Class<?> classTarget = pjp.getTarget().getClass();
		// 获取目标对象类型
		Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
		// 获取目标对象方法
		Method objMethod = classTarget.getMethod(methodName, par);
		// // 判断是否有自定义事务注解
		ExtTransaction declaredAnnotation = objMethod.getDeclaredAnnotation(ExtTransaction.class);
		if (declaredAnnotation == null) {
			System.out.println("您的方法上,没有加入注解!");
			return null;
		}
		return declaredAnnotation;
	}
	// 回滚事务
	public void isRollback(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
		// // 判断是否有自定义事务注解
		ExtTransaction declaredAnnotation = getExtTransaction(pjp);
		if (declaredAnnotation != null) {
			System.out.println("已经开始回滚事务");
			// 获取当前事务 直接回滚
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			return;
		}
	}
使用自定义注解
@ExtTransaction
public void add() {
userDao.add("test001", 20);
int i = 1 / 0;
System.out.println("################");
userDao.add("test002", 21);
}

4 事务的传播行为和隔离级别

4.1 事物传播行为

4.2 事务隔离级别

注解描述
@Transactional(isolation = Isolation.READ_UNCOMMITTED)读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED))读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)串行化

MYSQL: 默认为REPEATABLE_READ(可重复读)级别
SQLSERVER: 默认为READ_COMMITTED(读取已提交数据)