Spring事务小结

事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性;

Spring 实现事务管理的两种方式:

编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式事务管理中,必须在每个事务操作中包含额外的事务管理代码;

声明式事务管理(推荐):大多数情况下比编程式事务管理更好用,它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,Spring 声明式事务管理建立在 AOP 基础之上,是一个典型的横切关注点,通过环绕增强来实现,其原理是对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完毕之后根据执行情况提交或回滚事务,其模型如下:

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
	try {
		//开启事务
		return joinPoint.proceed();
		//提交事务
	} catch (Throwable e) {
		//回滚事务
		throw e;
	}finally {
		//释放资源
	}
}

如何实现声明式事务

1、添加spring-aspects-4.3.10.RELEASE.jar包

2、在Spring配置文件中添加如下配置:

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

 3、在Service层public方法上添加事务注解——@Transactional

package com.jd.coupon.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.jd.book.dao.IBookDao;
import com.jd.coupon.dao.ICouponDao;
import com.jd.exception.MoneyException;
import com.jd.money.dao.IMoneyDao;

@Service
@Transactional  //添加注解标记整个方法为一个事务
public class CouponService implements ICouponService {

	@Autowired
	private ICouponDao coupondao;
	
	@Autowired
	private IMoneyDao moneydao;
	
	@Autowired
	private IBookDao bookdao;

	@Override
	public boolean insert(String userId, String bookId, int count) {
		
		if(bookdao.enough(bookId, count)) {  //判断库存
			bookdao.update(bookId, count);  //修改库存
		}
		
		double price = bookdao.getPrice(bookId);
		double total = price*count;
		
		if(moneydao.enough(userId, total)) { //判断余额
			coupondao.order(userId, bookId, count);  //生成订单
			moneydao.update(userId, total);  //修改余额
		}
		return false;
	}
}

注意:

  1. 一个类含有 @Transactional 注解修饰的方法,则Spring框架自动为该类创建代理对象,默认使用JDK创建代理对象,可以通过添加<aop:aspectj-autoproxy proxy-target-class="true"/>使用CGLib创建代理对象,此时需要添加aspectjweaver-x.x.x.jar包;
  2. 不能在protected、默认或者private的方法上使用@Transactional注解,否则无效。

@Transactional注解属性:

1、rollbackFor和rollbackForClassName:指定对哪些异常回滚事务。默认情况下,如果在事务中抛出了运行时异常(继承自RuntimeException异常类),则回滚事务;如果没有抛出任何异常,或者抛出了检查时异常,则依然提交事务。这种处理方式是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式;但可以根据需要人为控制事务在抛出某些运行时异常时仍然提交事务,或者在抛出某些检查时异常时回滚事务;

2、noRollbackFor和noRollbackForClassName:指定对哪些异常不回滚事务;

3、readOnly:事务只读,指对事务性资源进行只读操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。由于只读的优化措施是在一个事务启动时由后端数据库实施的, 因此,只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法来说,将事务声明为只读才有意义;

4、timeout:设置一个事务所允许执行的最长时长(单位:秒),如果超过该时长且事务还没有完成,则自动回滚事务且出现org.springframework.transaction.TransactionTimedOutException异常;

5、propagation:指定事务传播行为,一个事务方法被另一个事务方法调用时,必须指定事务应该如何传播,例如:方法可能继承在现有事务中运行,也可能开启一个新事物,并在自己的事务中运行。Spring定义了如下7种事务传播行为(常用的为前两个):

  • REQUIRED:默认值,如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行;
  • REQUIRES_NEW:当前方法必须启动新事务,并在它自己的事务内运行,如果有事务在运行,则把当前事务挂起,直到新的事务提交或者回滚才恢复执行;
  • SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则以非事务的方式运行;

  • NOT_SUPPORTED:当前的方法不应该运行在事务中,如果有运行的事务,则将它挂起;

  • NEVER:当前方法不应该运行在事务中,否则将抛出异常;

  • MANDATORY(mandatory [ˈmændətɔːri] adj.强制的):当前方法必须运行在事务内部,否则将抛出异常;

  • NESTED(nest [nest] v.嵌套):如果有事务在运行,当前的方法在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行,此时等价于REQUIRED。注意:对于NESTED内层事务而言,内层事务独立于外层事务,可以独立递交或者回滚,如果内层事务抛出的是运行异常,外层事务进行回滚,内层事务也会进行回滚。

6、isolation:指定事务隔离级别,Spring定义了如下5种事务隔离级别:

  • DEFAULT:默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常为READ_COMMITTED;

  • READ_UNCOMMITTED:表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别可能出现脏读、不可重复读或幻读,因此很少使用该隔离级别;

  • READ_COMMITTED:表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,但可能出现不可重复读或幻读,这也是大多数情况下的推荐值;

  • REPEATABLE_READ:表示一个事务在整个过程中可以多次重复执行某个查询,且每次返回的记录都相同,除非数据被当前事务自生修改。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读,但可能出现幻读;

  • SERIALIZABLE:表示所有的事务依次逐个执行,事务之间互不干扰,该级别可以防止脏读、不可重复读和幻读,但是这将严重影响程序的性能,因此通常情况下也不会用到该级别;

  • 事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持:Oracle 支持READ_COMMITED和SERIALIZABLE两种事务隔离级别,默认为READ_COMMITED;MySQL 支持READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE四种事务隔离级别,默认为:REPEATABLE_READ。

发布了99 篇原创文章 · 获赞 3 · 访问量 1216

猜你喜欢

转载自blog.csdn.net/qq_44971038/article/details/104258826