Spring事务是一种用于管理数据库操作的机制,它确保一组相关的数据库操作要么全部成功提交,要么全部失败回滚。事务可以保证数据的一致性和完整性。
在Spring中,事务可以通过编程式事务管理和声明式事务管理两种方式来实现。编程式事务管理是通过编写代码来手动管理事务的开始、提交和回滚。而声明式事务管理是通过在方法或类级别上添加注解来指定事务的属性,Spring框架会自动管理事务的开始、提交和回滚。
Spring事务提供了以下特性:
原子性(Atomicity):事务中的所有操作要么全部成功提交,要么全部失败回滚。
一致性(Consistency):事务在执行前和执行后,数据库的状态保持一致。
隔离性(Isolation):事务之间相互隔离,一个事务的操作不会对其他事务产生影响。
持久性(Durability):事务一旦提交,对数据库的修改将永久保存。
Spring事务可以应用于各种数据访问技术,包括关系型数据库(如MySQL、Oracle等)、NoSQL数据库(如MongoDB、Redis等)以及消息队列等。通过使用Spring事务,可以简化数据库操作的管理,并确保数据的一致性和完整性。
提示:以下是本篇文章正文内容,下面案例可供参考
DEFAULT(默认):使用底层数据库的默认隔离级别。对于大多数数据库来说,通常是READ_COMMITTED。
READ_UNCOMMITTED(读未提交):最低的隔离级别,允许一个事务读取另一个事务未提交的数据。这种隔离级别可能导致脏读、不可重复读和幻读的问题。
READ_COMMITTED(读已提交):保证一个事务只能读取到已经提交的数据。这种隔离级别可以避免脏读问题,但仍可能出现不可重复读和幻读的问题。
REPEATABLE_READ(可重复读):保证一个事务在执行期间多次读取同一数据时,能够得到一致的结果。这种隔离级别可以避免脏读和不可重复读问题,但仍可能出现幻读的问题。
SERIALIZABLE(串行化):最高的隔离级别,确保事务串行执行,避免了脏读、不可重复读和幻读的问题。但是,这种隔离级别的性能较低,通常不推荐在高并发环境下使用
如果你想设置其他隔离级别,可以通过注解形式进行设置
@Transactional(isolation = Isolation.READ_COMMITTED)
public void someTransactionalMethod() {
// 事务操作
}
REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常用的传播行为,适用于大多数情况。
SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行。适用于不需要事务支持的方法,但可以参与到已经存在的事务中。
MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。适用于必须在事务中执行的方法,如果没有事务则会抛出异常。
REQUIRES_NEW:创建一个新的事务,并挂起当前事务(如果存在)。适用于需要独立的事务执行的方法,不受外部事务的影响。
NOT_SUPPORTED:以非事务的方式执行方法,如果当前存在事务,则挂起该事务。适用于不需要事务支持的方法,且不希望受到外部事务的影响。
NEVER:以非事务的方式执行方法,如果当前存在事务,则抛出异常。适用于不允许在事务中执行的方法,如果存在事务则会抛出异常。
NESTED:如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一部分,它可以独立提交或回滚,但最终会受到外部事务的提交或回滚的影响。
如果你想设置其他传播机制,可以通过注解形式进行设置
@Transactional(propagation = Propagation.REQUIRED)
public void someTransactionalMethod() {
// 事务操作
}
例子说明:
A添加成功,B被回滚。
// serviceA
public void insertA() {
mapperA.insert()
}
// serviceB
@Transactional(propagation = Propagation.REQUIRED)
public void insertB() {
mapperB.insert()
throw new RuntimeException("抛出异常")
}
public void test() {
serviceA.insertA() // 没有事务
serviceB.insertB() // 开启事务
}
A不会发生回滚
// serviceA
public void insertA() {
mapperA.insert()
}
// serviceB
public void insertB() {
mapperB.insert()
throw new RuntimeException("抛出异常")
}
public void test() {
serviceA.insertA() // 开启事务
serviceB.insertB() // B不开事务
}
A添加成功,B被回滚。
// serviceA
@Transactional(propagation = Propagation.REQUIRED)
public void insertA() {
mapperA.insert()
}
// serviceB
@Transactional(propagation = Propagation.REQUIRED)
public void insertB() {
mapperB.insert();
throw new RuntimeException("抛出异常") // 此处抛出异常类型需为RuntimeException(运行时异常)或子类才会实现事务的回滚,Exception并不会回滚
}
public void test() {
serviceA.insertA(); // 开启事务A
serviceB.insertB(); // 开启事务B
}
@Service
public class BankService {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private AccountRepository accountRepository;
public void transferMoney(String fromAccount, String toAccount, double amount) {
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
Account to = accountRepository.findByAccountNumber(toAccount);
accountRepository.save(to);
transactionManager.commit(transactionStatus);
} catch (Exception e) {
transactionManager.rollback(transactionStatus);
throw e;
}
}
}
@Service
@Transactional
public class BankService {
@Autowired
private AccountRepository accountRepository;
public void transferMoney(String fromAccount, String toAccount, double amount) {
Account from = accountRepository.findByAccountNumber(fromAccount);
Account to = accountRepository.findByAccountNumber(toAccount);
accountRepository.save(to);
}
}
这里主要还是讲注解事务源码为主
在springboot项目中,如果我们要开启注解事务,一般会加上@EnableTransactionManagement注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
@EnableTransactionManagement
当我们加上@EnableTransactionManagement注解时,它会做以下几件事情:
1.自动检测并注册TransactionManagementConfigurer实现类:@EnableTransactionManagement注解会自动检测Spring容器中是否存在实现了TransactionManagementConfigurer接口的Bean。如果存在,会将其注册到Spring容器中,用于配置事务管理器。
2…启用基于注解的事务管理:@EnableTransactionManagement注解会启用基于注解的事务管理,即可以使用@Transactional注解来标记需要进行事务管理的方法。
3.注册事务增强器:@EnableTransactionManagement注解会注册一个TransactionInterceptor实例作为事务增强器。该事务增强器会拦截带有@Transactional注解的方法,并在方法执行前后进行事务的开始、提交和回滚操作。
4.配置事务管理器:@EnableTransactionManagement注解会自动配置一个适合的事务管理器。它会根据Spring容器中的配置和环境来选择合适的事务管理器,如DataSourceTransactionManager、JpaTransactionManager等。
总之,@EnableTransactionManagement注解的作用是启用Spring的事务管理功能,并自动配置事务管理器、注册事务增强器,以及启用基于注解的事务管理。通过添加该注解,可以方便地使用@Transactional注解来管理事务,提高开发效率。
/**
* 创建一个事务拦截器的Bean,用于拦截带有@Transactional注解的方法,并在方法执行前后进行事务的开始、提交和回滚操作。
* 该拦截器使用给定的事务属性源来确定事务的属性。
* 如果存在事务管理器,则将其设置到拦截器中。
* @param transactionAttributeSource 事务属性源,用于确定事务的属性
* @return 事务拦截器的实例
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
TransactionInterceptor 类实现了MethodInterceptor接口,它是由CGLib库提供的。它用于在方法调用前后进行拦截和处理。通过实现MethodInterceptor接口,我们可以在目标方法被调用前后添加额外的逻辑或修改方法的行为。在方法调用时,MethodInterceptor会拦截目标方法的调用,并在调用前后执行自定义的逻辑。这使得我们可以在不修改原始代码的情况下,对方法进行增强和控制。
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
// 获取目标类的Class对象,可能为null。
// 事务属性源应该传递目标类和方法,方法可能来自接口。
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 适应TransactionAspectSupport的invokeWithinTransaction方法...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
// 执行原始方法的逻辑
return invocation.proceed();
}
@Override
public Object getTarget() {
// 获取目标对象
return invocation.getThis();
}
@Override
public Object[] getArguments() {
// 获取方法参数
return invocation.getArguments();
}
});
}
也就意味着,在spring扫描到使用@Transactional的类时,会对该类进行代理,执行方法增强和环绕通知。
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 获取事务属性源
// 该类有一个属性publicMethodsOnly,为true,限制了只有public方法,@Transcational注解才能生效
TransactionAttributeSource tas = getTransactionAttributeSource();
// 获取事务属性,包括传播级别、隔离级别、回滚规则等
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 获取事务管理类,一般用的是JDBC的DataSourceTransactionManager
// 这里有本地缓存,只有第一次获取才会加载bean
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 获取全路径方法名,用于监控和记录
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建TransactionInfo(这里的TransactionInfo是TransactionAspectSupport的内部类)
// 这里会处理事务的传播级别,同时将事务和线程绑定
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 目标方法执行异常,事务回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 重置线程的事务信息
cleanupTransactionInfo(txInfo);
}
// 事务提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 省略CallbackPreferringPlatformTransactionManager的处理
// 这部分是编程式事务的处理
......
}
}
创建事务:根据给定的属性尝试创建一个事务,并将相关的事务信息TransactionStatus绑定到TransactionInfo返回,同时会将当前的TransactionInfo对象绑定到当前线程中,将该线程之前的事务信息记录到TransactionInfo的oldTransactionInfo属性中,也就是记录外层事务,可以形成不同方法之间的事务信息链表。
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
//如果没有名称,将方法全路径作为事务名称
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 在该方法决定是否开启一个事务,
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 通过各参数生成TransactionInfo,并记录线程中上个方法的TransactionInfo,将新TransactionInfo和线程绑定
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
// 获取事务的数据源连接
// 设置是否允许保存点
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
if (isExistingTransaction(transaction)) {
// 如果事务对象的是否存在数据库连接且已经开启过事务,则说明存在外层事务
// 根据不同的事务传播级别进行处理
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// 若不存在外层事务,开始创建新事务
// 判断是否超时
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// PROPAGATION_MANDATORY传播级别是:如果存在事务,则将当前方法加入到该事务中,如果不存在事务则当前方法抛出异常
// 这里由于不存在外层事务,所以直接抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// 处理PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED三种传播级别的事务
// 这三种传播级别的共同点是如果当前不存在事务,则创建一个新的事务
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 挂起给定的事务
// 首先挂起事务同步器,清空TransactionSynchronizationManager中保存的当前线程的事务信息,返回被挂起的资源信息
// 核心方法是doSuspend,由不同子类实现,DataSourceTransactionManage的实现就是将当前线程的连接资源解绑
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
// 判断是否需要开启事务同步,默认是开启
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 真正开启事务
doBegin(transaction, definition);
// 根据需要初始化事务同步器
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// 处理PROPAGATION_SUPPORTS、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED三种传播级别的事务
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 这里并没有真正开启一个事务,只创建了一个DefaultTransactionStatus对象用于记录和初始化事务同步器
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
doBegin真正开启一个事务
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 事务没有连接资源或者资源被标记为和事务同步,则获取一个新的连接
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 获取新的数据库连接
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 将新的连接信息保存到事务对象中
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 将synchronizedWithTransaction设置为true,即资源标记为与事务同步
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 设置数据库连接的隔离级别和只读标志
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
// 设置事务的隔离级别属性
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// 将自动提交设置为手动提交
if (con.getAutoCommit()) {
// 设置mustRestoreAutoCommit为true,用于事务提交后回复自动提交
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 到这里事务已经开启,后续的sql需要等待commit命令执行后才会提交
// 对于只读事务,执行"SET TRANSACTION READ ONLY"优化
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
// 设置超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 将连接资源和当前线程绑定
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
这里就和编程式事务一致了,执行方法,捕获异常,有异常就进行回滚处理。finally 就是将本次事务对象中的外层事务oldTransactionInfo重新和线程绑定。最后在提交事务
try {
// 执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 目标方法执行异常,事务回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 重置线程的事务信息
cleanupTransactionInfo(txInfo);
}
// 事务提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
1.事务方法内部调用另一个方法:如果在事务方法内部调用了另一个方法,而被调用的方法没有添加事务注解,那么事务将失效。因为Spring的事务是基于AOP实现的,只有通过代理对象调用的方法才会被事务管理器拦截,从而开启事务。如果调用 bankService.B();则不会失效
@Service
@Transactional
public class BankService {
@Autowired
private AccountRepository accountRepository;
@Autowired
public BankService bankService ;
public void transferMoney(String fromAccount, String toAccount, double amount) {
Account from = accountRepository.findByAccountNumber(fromAccount);
Account to = accountRepository.findByAccountNumber(toAccount);
accountRepository.save(to);
B();
bankService.B();
}
public void B(){
}
}
2.异常被捕获并处理:如果在事务方法内部捕获了异常并进行了处理,那么事务将失效。当异常被捕获后,Spring事务管理器无法感知到异常的存在,因此无法触发事务回滚。
@Service
@Transactional
public class BankService {
@Autowired
private AccountRepository accountRepository;
@Autowired
public BankService bankService ;
public void transferMoney(String fromAccount, String toAccount, double amount) {
try{
Account from = accountRepository.findByAccountNumber(fromAccount);
Account to = accountRepository.findByAccountNumber(toAccount);
accountRepository.save(to);
}catch(Exception e){
}
}
}
3.事务方法被非public修饰:如果事务方法被非public修饰,那么事务将失效。因为Spring的事务是通过代理对象来实现的,而非public方法无法被代理对象拦截,从而无法开启事务.
@Service
@Transactional
public class BankService {
@Autowired
private AccountRepository accountRepository;
@Autowired
public BankService bankService ;
private void transferMoney(String fromAccount, String toAccount, double amount) {
try{
Account from = accountRepository.findByAccountNumber(fromAccount);
Account to = accountRepository.findByAccountNumber(toAccount);
accountRepository.save(to);
}catch(Exception e){
}
}
}
4.事务方法内部捕获了RuntimeException:如果在事务方法内部捕获了RuntimeException,并且没有重新抛出或处理该异常,那么事务将失效。因为Spring事务默认只对未被捕获的RuntimeException进行回滚。
// 提交事务时,会捕获RuntimeException 异常,不做处理
public void completeTransactionAfterThrowing(Exception ex) {
try {
// 回滚事务
transactionManager.rollback();
// 记录异常日志
logger.error("事务回滚完成,异常信息:" + ex.getMessage());
// 发送通知或执行其他操作
}catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2
}