@Transactional失效及事务传播特性

@Transactional失效及事务传播特性

1、@Transactional注解失效

@Transactional注解不起作用的场景:

1、带有@Transactional注解的方法不是public方法

2、在同一个类中的非@Transactional方法调用了带有@Transactional注解的方法

3、在带有@Transactional注解的方法内部捕获了异常,但没有抛出新的异常

下面根据@Transactional的原理分析上述三种场景下为什么@Transactional不起作用。

1.1、非public方法

1、若带有@Transactional注解的方法不是public方法,则事务不起作用

根据@Transactional的原理分析可知,在BeanFactoryTransactionAttributeSourceAdvisor类的getTransactionAttributeSource()方法中会调用computeTransactionAttribute方法来获取TransactionAttribute,在computeTransactionAttribute方法中进行了判断,默认只对public方法进行事务代理。如果不是public方法,则返回null,若TransactionAttribute是null,则认为方法上没有@Transactional注解。如下源码所示。

下面是 TransactionAttributeSourcePointcut 进行匹配的源码:

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass) {
		if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
			return false;
		}
		//获取 BeanFactoryTransactionAttributeSourceAdvisor 中的AnnotationTransactionAttributeSource
		TransactionAttributeSource tas = getTransactionAttributeSource();
		//判断TransactionAttribute不为空,说明该targetClass的method是有事务注解的
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}
}

调用AbstractFallbackTransactionAttributeSource的getTransactionAttribute方法:

    public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
		if (method.getDeclaringClass() == Object.class) {
			return null;
		}

		// First, see if we have a cached value.
		//构建一个缓存key
		Object cacheKey = getCacheKey(method, targetClass);
		//从缓存中获取TransactionAttribute 
		TransactionAttribute cached = this.attributeCache.get(cacheKey);
		if (cached != null) {
			// Value will either be canonical value indicating there is no transaction attribute,
			// or an actual transaction attribute.
			if (cached == NULL_TRANSACTION_ATTRIBUTE) {
				return null;
			}
			else {
			//返回缓存中的TransactionAttribute
				return cached;
			}
		}
		else {
			// We need to work it out.
			//查找和解析method的事务注解,封装成TransactionAttribute
			TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
			// Put it in the cache.
			if (txAttr == null) {
				this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
			}
			else {
				String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
				if (txAttr instanceof DefaultTransactionAttribute) {
					((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
				}
				//把 TransactionAttribute  放入缓存attributeCache
				this.attributeCache.put(cacheKey, txAttr);
			}
			return txAttr;
		}
	}

看一下 computeTransactionAttribute 方法的源码:

    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
		// Don't allow no-public methods as required.
		//默认只能是public方法进行事务代理,但是可以通过allowPublicMethodsOnly方法修改
		if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
			return null;
		}

		// The method may be on an interface, but we need attributes from the target class.
		// If the target class is null, the method will be unchanged.
		//有可能找到的是接口方法,但是需要的是实现类的方法
		Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

		// First try is the method in the target class.
		//【重要】首先从方法上找事务注解,有就返回,没有再找类上的事务注解,所以方法上的事务注解优先级高
		TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
		if (txAttr != null) {
			return txAttr;
		}

		// Second try is the transaction attribute on the target class.
		//[重要]然后从当前类上找注解
		txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
		if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
			return txAttr;
		}
		//如果最终的目标方法和当前方法不一致,从当前方法上找
		if (specificMethod != method) {
			//回退是看原方法
			// Fallback is to look at the original method.
			txAttr = findTransactionAttribute(method);
			if (txAttr != null) {
				return txAttr;
			}
			// Last fallback is the class of the original method.
			//最后一个回退是原始方法的类
			txAttr = findTransactionAttribute(method.getDeclaringClass());
			if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
				return txAttr;
			}
		}
		//实在找不到,返回一个null,该方法不会被代理
		return null;
	}

1.2、同类中方法调用

2、若在同一个类中的非@Transactional方法调用了带有@Transactional注解的方法,则事务不起作用

@Component
public class TestServiceImpl implements TestService {
    @Resource
    TestMapper testMapper;

    //正常public修饰符的事务方法
    @Transactional
    public void methodA() {
        int re = testMapper.insert(new Test(10,20,30));
        if (re > 0) {
            throw new RuntimeException("插入失败");
        }
    }

    public void methodB(){
        //类内部调用@Transactional标注的方法。
        this.methodA();
    }

}

由上述示例代码所示,由于methodB方法没有被@Transactional注解修饰,不会生成与该方法对应的拦截器,所以TransactionInterceptor不会对该方法进行事务处理。由于methodA方法带有@Transactional注解,所以AOP动态代理会为TestServiceImpl类生成一个代理对象,在代理对象中对methodA和methodB方法进行了重写。由JDK动态代理的原理可知,在这两个重写的方法内部分别调用了JdkDynamicAopProxy的invoke方法。JdkDynamicAopProxy的源码如下所示:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	//创建代理
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		//创建代理类
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

	//...省略其他代码...
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//方法调用
		MethodInvocation invocation;
		Object oldProxy = null;
		boolean setProxyContext = false;
		
		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			//省略了其他代码
			Object retVal;
			
			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			//目标类
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// Get the interception chain for this method.
			//获取此方法的拦截器通知链
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			if (chain.isEmpty()) {
			     //如果没有拦截器能应用到该方法,直接反射调用方法
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				//创建 ReflectiveMethodInvocation 方法调用
				// We need to create a method invocation...
				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				//通过拦截器链进入连接点,调用增强方法
				retVal = invocation.proceed();
			}

			//省略其他代码
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}
}

invoke方法中首先获取方法的拦截器通知链,如果有就会创建 MethodInvocation来调用方法,应用通知,如果没有拦截器链就直接反射调用方法。所以在JdkDynamicAopProxy的invoke方法中通过反射直接调用目标类的methodB方法,并没有为该方法进行事务处理。

解决方法:为methodB方法添加@Transactional注解。

在同一个类中方法间的调用是通过this实现的,只是简单的方法调用,并未调用代理对象的methodA方法,methodB方法其实就是下面这种形式:

public void methodB(){
int re = testMapper.insert(new Test(10,20,30));
    if (re > 0) {
        throw new RuntimeException("插入失败");
    }
}

所以另一种解决方法是:在TestServiceImpl中注入自己(实际是代理对象),通过代理对象调用methodA方法,如下所示:

@Autowired
Private TestServiceImpl testServiceImpl;

public void methodB(){
    //类内部调用@Transactional标注的方法。
    testServiceImpl.methodA();
}

1.3、方法内部捕获异常

3、在带有@Transactional注解的方法内部捕获了异常,但没有抛出新的异常

    @Transactional
    public void methodA() {
        try{
            int re = testMapper.insert(new Test(10,20,30));
        	if (re > 0) {
            	throw new RuntimeException("抛出异常");
            }
        } catch(Exception e){
            System.out.println("插入失败");
        }
    }

如上所示,public方法methodA虽然被@Transactional注解修饰了,但是在该方法内部捕获了异常,且没有抛出新的异常。由事务原理可知,在TransactionInterceptor的invoke方法中,调用了父类TransactionAspectSupport的invokeWithinTransaction方法,该方法的源码如下所示:

public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {

/**
	 * General delegate for around-advice-based subclasses, delegating to several other template
	 * methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
	 * as well as regular {@link PlatformTransactionManager} implementations.
	 * @param method the Method being invoked
	 * @param targetClass the target class that we're invoking the method on
	 * @param invocation the callback to use for proceeding with the target invocation
	 * @return the return value of the method, if any
	 * @throws Throwable propagated from the target invocation
	 */
	@Nullable
	protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		// If the transaction attribute is null, the method is non-transactional.
		//获取事务属性源:即 @Transactional注解的属性
		TransactionAttributeSource tas = getTransactionAttributeSource();
		//获取事务属性
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		//事务管理器
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		//方法名,类名+方法名:如 cn.xx.UserServiceImpl.save
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		//声明式事务处理 @Transactional
		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// 【标记1】Standard transaction demarcation with getTransaction and commit/rollback calls.
			//创建TransactionInfo事务详情对象,其中包括事务管理器(transactionManager),事务属性(transactionAttribute),方法名(joinpointIdentification),事务状态(transactionStatus)
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			Object retVal = null;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				//【标记2】执行方法,这是一个环绕通知,通常会导致目标对象被调用
				retVal = invocation.proceedWithInvocation();
			} catch (Throwable ex) {
				// target invocation exception
				//回滚事务:AfterThrowing
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			} finally {
				//清理事务
				cleanupTransactionInfo(txInfo);
			}
			//AfterReturning:后置通知,提交事务
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}
		else {
			//省略了其他代码
		}
	}
}

如果目标方法中抛出了异常,在invokeWithinTransaction方法中会捕获并回滚事务。但是如果目标方法自己处理了异常,在invokeWithinTransaction方法中就无法获取到异常,事务自然就无法回滚,就会正常提交。

1.4、未使用代理对象

如果没有使用代理对象,而是使用普通bean对象,也会导致@Transactional失效。

如下案例所示,有一个接口StatisticsHandler,该接口有两个实现类UserStatisticsHandler(用户数统计处理器)和FlowStatisticsHandler(流量统计处理器),分别用于统计用户数和流量数。基于策略模式,根据不同的统计类型执行相应的方法。


public interface StatisticsHandler {
    /**
     * 初始化 StatisticsHandlerHolder 类中的 Map
     */
    void initMap();

    /**
     * 统计数量
     */
    void statisticsCount();
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class UserStatisticsHandler implements StatisticsHandler {
    @Autowired
    private StatisticsHandlerHolder statisticsHandlerHolder;

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 注意:此处的this是普通bean对象,如果通过this调用statisticsCount方法,会使得@Transactional不生效。
     * 为了使事务生效,可以从spring容器中获取代理对象保存到holder容器中,从holder中获取的就是代理对象。
     */
    @Override
    @PostConstruct
    public void initMap() {
        // statisticsHandlerHolder.getHandlerMap().putIfAbsent(StatisticsTypeEnum.USER.name(), this);
        statisticsHandlerHolder.getHandlerMap()
            .putIfAbsent(StatisticsTypeEnum.USER.name(), applicationContext.getBean(UserStatisticsHandler.class));
    }

    @Override
    @Transactional
    public void statisticsCount() {
        // todo
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class FlowStatisticsHandler implements StatisticsHandler {
    @Autowired
    private StatisticsHandlerHolder statisticsHandlerHolder;

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 注意:此处的this是普通bean对象,如果通过this调用statisticsCount方法,会使得@Transactional不生效。
     * 为了使事务生效,可以从spring容器中获取代理对象保存到holder容器中,从holder中获取的就是代理对象。
     */
    @Override
    public void initMap() {
        // statisticsHandlerHolder.getHandlerMap().putIfAbsent(StatisticsTypeEnum.FLOW.name(), this);
        statisticsHandlerHolder.getHandlerMap()
            .putIfAbsent(StatisticsTypeEnum.FLOW.name(), applicationContext.getBean(FlowStatisticsHandler.class));
    }

    @Override
    @Transactional
    public void statisticsCount() {
        // todo
    }
}

import lombok.Getter;
import lombok.Setter;

import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Getter
@Setter
@Service
public class StatisticsHandlerHolder {
    /**
     * 用于维护统计类型与统计处理器的关系,key是统计类型
     */
    private Map<String, StatisticsHandler> handlerMap = new ConcurrentHashMap<>();

    /**
     * 获取统计处理器
     *
     * @param type 统计类型
     * @return 统计处理器
     */
    public StatisticsHandler getStatisticsHandler(StatisticsTypeEnum type) {
        return handlerMap.get(type.name());
    }
}

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

@Service
public class StatisticsService {
    @Autowired
    private StatisticsHandlerHolder statisticsHandlerHolder;

    public void statisticsCount(StatisticsTypeEnum type) {
        StatisticsHandler statisticsHandler = statisticsHandlerHolder.getStatisticsHandler(type);
        // 如果使用普通bean对象会导致事务失效,需要使用代理对象
        statisticsHandler.statisticsCount();
    }
}
public enum StatisticsTypeEnum {
    USER,
    FLOW
}

2、@Transactional不起作用的解决方法

pom.xml文件:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>

启动类:

package com.tx.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * @Author: 倚天照海
 * @EnableTransactionManagement注解用于开启事务
 * EnableAspectJAutoProxy(exposeProxy=true)表示开启AOP代理,通过spring管理带
 * 有@Aspect注解的类的创建,暴露代理对象,通过aopContext能够访问代理对象
 */
@SpringBootApplication
@EnableTransactionManagement
@EnableAspectJAutoProxy(exposeProxy = true)
public class TxStudyApplication {

    public static void main(String[] args) {
        SpringApplication.run(TxStudyApplication.class,args);
    }

}

Controller类:

package com.tx.study.controller;

import com.tx.study.pojo.OrderInfoDto;
import com.tx.study.service.TxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: 倚天照海
 */
@RestController
@RequestMapping(value = "/tx")
public class TxController {

    @Autowired
    private TxService txService;

    @PostMapping(value = "/order")
    public void createOrderAndInfo(@RequestBody OrderInfoDto orderInfoDto){
        txService.createOrderAndInfo(orderInfoDto);
    }

}

Service类:

package com.tx.study.service;

import com.tx.study.dao.TxDao;
import com.tx.study.pojo.Order;
import com.tx.study.pojo.OrderInfo;
import com.tx.study.pojo.OrderInfoDto;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

/**
 * @Author: 倚天照海
 * 解决@Transactional注解不起作用的问题
 */
@Service
public class TxService {

    @Autowired
    private TxDao txDao;
    //方式一:自己注入自己
    @Autowired
    private TxService txServiceProxy;

    @Autowired
    private ApplicationContext applicationContext;

    public void createOrderAndInfo(OrderInfoDto orderInfoDto) {
        //方式二:通过spring应用上下文获取bean实例
        //TxService txServiceProxy = applicationContext.getBean(TxService.class);
        //方式三:通过aspectj暴露代理对象,需要引入<groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId>依赖
        //TxService txServiceProxy = (TxService) AopContext.currentProxy();
        Order order = new Order();
        order.setOrderId(orderInfoDto.getOrderId());
        order.setUsername(orderInfoDto.getUsername());
        order.setOrderTime(orderInfoDto.getOrderTime());
        //保存订单数据
        txServiceProxy.createOrder(order);
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderId(orderInfoDto.getOrderId());
        orderInfo.setOrderName(orderInfoDto.getOrderName());
        orderInfo.setGoods(orderInfoDto.getGoods());
        orderInfo.setDescribe(orderInfoDto.getDescribe());
        try {
            //保存订单信息数据,如果出现异常,不能影响订单数据的保存
            txServiceProxy.createOrderInfo(orderInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void createOrder(Order order){
        String sql = "insert into t_order(order_id,username,order_time) values(?,?,?)";
        if (ObjectUtils.isEmpty(order)){
            return;
        }
        txDao.createOrder(sql,order);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void createOrderInfo(OrderInfo orderInfo){
        String sql = "insert into t_order_info(order_id,order_name,goods,order_describe) values(?,?,?,?)";
        if (ObjectUtils.isEmpty(orderInfo)){
            return;
        }
        txDao.createOrderInfo(sql,orderInfo);
        //模拟异常
        for (int i=1;i>=0;i--){
            int num = 1/i;
        }
    }

}

Dao类:

package com.tx.study.dao;

import com.tx.study.pojo.Order;
import com.tx.study.pojo.OrderInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

/**
 * @Author: 倚天照海
 */
@Repository
public class TxDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void createOrder(String sql, Order order){
        String orderId = order.getOrderId();
        String username = order.getUsername();
        String orderTime = order.getOrderTime();
        Object[] params = new Object[]{orderId,username,orderTime};
        jdbcTemplate.update(sql,params);
    }

    public void createOrderInfo(String sql, OrderInfo orderInfo){
        String orderId = orderInfo.getOrderId();
        String orderName = orderInfo.getOrderName();
        String goods = orderInfo.getGoods();
        String describe = orderInfo.getDescribe();
        Object[] params = new Object[]{orderId,orderName,goods,describe};
        jdbcTemplate.update(sql,params);
    }

}

由下图可知,this对象并不是代理对象,自动注入的txServiceProxy才是代理对象。

3、事务传播特性

事务传播特性或行为就是多个事务方法相互调用时,事务如何在这些方法间传播。

1.PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择,是spring默认的事务传播行为。

2.PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。

3.PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作,即新建一个事务。

4.PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

5.PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6.PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。

7.PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

假设有两个方法:ServiceA.methodA和ServiceB.methodB,ServiceA和ServiceB是两个类,在ServiceA.methodA方法中调用ServiceB.methodB方法。

1、如果methodA方法没有事务,methodB方法的事务类型是PROPAGATION_REQUIRED,即方法上使用@Transactional(propagation=Propagation.REQUIRED)注解,则methodB方法会新建一个事务。

2、如果ServiceA.methodA方法的事务行为类型是PROPAGATION_REQUIRED,即方法上使用@Transactional(propagation=Propagation.REQUIRED)注解,ServiceB.methodB方法上也使用@Transactional(propagation=Propagation.REQUIRED)注解,则ServiceB.methodB方法不会新建事务,而是加入到ServiceA.methodA方法的事务中。并且这两个方法的事务会互相影响,也就是说,如果这两个方法中有一个方法中抛出异常,则这两个方法的事务都会回滚。即使在ServiceA.methodA方法中调用ServiceB.methodB方法的地方使用try…catch捕获处理,如果ServiceB.methodB方法中抛出异常,则这两个方法的事务都会回滚。

3、如果ServiceA.methodA方法上使用@Transactional(propagation=Propagation.REQUIRED),ServiceB.methodB方法上使用@Transactional(propagation=Propagation.REQUIRES_NEW),则ServiceB.methodB方法会新建一个事务,并将ServiceA.methodA方法的事务挂起。并且ServiceB.methodB方法的事务会影响ServiceA.methodA方法的事务,而ServiceA.methodA方法的事务不会影响ServiceB.methodB方法的事务,也就是说,如果ServiceB.methodB方法中抛出了异常,则ServiceB.methodB方法的事务会回滚,如果在ServiceA.methodA方法中调用ServiceB.methodB方法的地方没有使用try…catch捕获处理,则ServiceA.methodA方法的事务也会回滚,这相当于在ServiceA.methodA方法中也抛出了异常。如果ServiceA.methodA方法中抛出了异常,而ServiceB.methodB方法中没有抛出异常,则ServiceA.methodA方法的事务会回滚,而ServiceB.methodB方法的事务可以正常提交。

4、如果ServiceA.methodA方法上使用@Transactional(propagation=Propagation.REQUIRED),ServiceB.methodB方法上使用@Transactional(propagation=Propagation.NESTED),则methodB方法的事务嵌套在methodA方法的事务内执行,并且methodA方法的事务会影响methodB方法的事务,而methodB方法的事务不一定会影响methodA方法的事务,需要根据在methodA方法中是否使用try…catch捕获处理methodB方法来进行判断。如果methodB方法抛出异常,并且在methodA方法中调用methodB方法的地方使用try…catch捕获处理,则methodB方法的事务会回滚,methodA方法的事务不会回滚,而是正常提交。如果在methodA方法中调用methodB方法的地方没有使用try…catch捕获处理,则methodA方法的事务也会回滚。如果methodB方法没有抛出异常,而methodA方法抛出异常,则这两个方法的事务都会回滚。Propagation.NESTED事务的提交要等待与其外层父事务一起提交。

5、如果methodA方法有事务,methodB方法没有事务,methodA方法调用methodB方法,无论这两个方法是否在同一个类中, 都只会共用一个事务,要么都提交,要么都回滚。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`try-catch` 和 `@Transactional` 是两种不同的机制,用于处理异常和数据库事务管理,通常在JavaSpring框架中使用。它们在某些情况下可能会失效或不协同工作,这通常发生在以下情况: 1. **嵌套事务**:如果在一个`@Transactional`方法内部又有一个`try-catch`块,Spring默认不会自动开始另一个事务,所以`catch`部分可能不会在事务上下文中执行。要解决这个问题,你需要显式地开启一个新的事务。 ```java @Transactional(propagation = Propagation.REQUIRES_NEW) try { // ... } catch (Exception e) { // 异常处理 } ``` 2. **事务回滚策略**:`@Transactional`注解提供了回滚规则(如`rollbackFor`),如果异常匹配指定的条件,事务才会回滚。如果`try-catch`中的异常类型不满足这个规则,`catch`部分的代码可能在已经完成的事务中运行,导致数据状态不一致。 3. **异常传播**:`@Transactional`中的`throwable`属性定义了异常如何在事务边界间传播。如果`catch`块抛出了一个未处理的异常,这可能会影响`@Transactional`行为,特别是当`propagation`设置为`REQUIRED`时。 4. **异步处理**:如果`catch`中的代码包含异步任务(如发送邮件或调用远程服务),由于这些操作可能不在Spring事务管理范围之内,`@Transactional`的效果可能会受限。 为了确保`try-catch`与`@Transactional`的正确协同,建议在`catch`块中明确处理事务(如显式回滚或关闭事务)并考虑异常传播规则。同时,如果遇到问题,确保检查你的配置和代码逻辑是否正确使用了这两个特性。如果有疑问,可以详细查看Spring文档或使用Spring提供的AOP(面向切面编程)工具来调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值