@Transactional失效

58 阅读2分钟

1.Transactional的实现流程

  1. 当方法被访问时,请求会通过AOP被TransactionInterceptor拦截并调用invokeWithinTransaction
  2. invokeWithinTransaction中用于获得目标方法的事务属性,属性包含如何管理事务的相关配置
TransactionAttributeSource tas = this.getTransactionAttributeSource();
TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
  1. 确定用于管理当前事务的事务管理器,下文针对于平台事务
TransactionManager tm = this.determineTransactionManager(txAttr);

事务管理器分为反应式事务管理器(ReactiveTransactionManager)和平台事务管理器(PlatformTransactionManager),前者需要继承AbstractReactiveTransactionManager或实现ReactiveTransactionManager,是为了实现响应式编程设计的,而后者支持持久化存储方案,内置有:DataSourceTransactionManager用于JDBC事务管理、JpaTransactionManager用于JPA、JtaTransactionManager用于JTA

  1. 若需要,则创建事务
TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm) {
    status = tm.getTransaction((TransactionDefinition)txAttr);
}

5.最后执行原方法

retVal = invocation.proceedWithInvocation();

2.Transactional作用的方法无法被重写

当方法的访问修饰符不是public,或方法被final修饰时,@Transactional会失效

@Transactional
private Result<List<Patient>> getPatients() {
    return Result.success(patientMapper.selectList(null));
}
@Transactional
public final Result<List<Patient>> getPatients() {
    return Result.success(patientMapper.selectList(null));
}

因为Transactional是通过AOP实现对目标方法的增强,需要通过jdk动态代理或Cglib生成代理类,若该方法被private或final修饰,则无法重写该方法,导致无法创建代理类。

public abstract class TransactionAspectSupport {
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
        TransactionAttributeSource tas = this.getTransactionAttributeSource();
        TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
    }
}

通过getTransactionAttribute()获得目标方法的事务属性

public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource{
    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
        if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        }
    }
}

当修饰符不是public的时候,就返回空的事务属性,因此不会提供事务

3. 类方法的直接调用

@Override
public Result<List<Patient>> getPatients() {
    Patient patient1 = getPatientById(1).getData();
    Patient patient2 = getPatientById(2).getData();
    return Result.success(List.of(patient1, patient2));
}

@Override
@Transactional
public Result<Patient> getPatientById(Integer id) {
    Patient patient = patientMapper.selectOne(Wrappers.<Patient>lambdaQuery().eq(Patient::getId, id));
    return Result.success(patient);
}

getPatientById()方法实际上为this.getPatientById(),由于事务是通过获得代理对象来实现的,直接调用对象内的方法,不会生成代理对象,导致无法实现事务的功能

解决方法

  1. 新建一个Service方法,将需要交给事务管理的方法放进去

  2. 注入自己,在调用方法时,通过注入的对象中的方法

@Autowired
private PatientService patientService;

@Override
public Result<List<Patient>> getPatients() {
    Patient patient1 = patientService.getPatientById(1).getData();
    Patient patient2 = patientService.getPatientById(2).getData();
    return Result.success(List.of(patient1, patient2));
}

@Override
@Transactional
public Result<Patient> getPatientById(Integer id) {
    Patient patient = patientMapper.selectOne(Wrappers.<Patient>lambdaQuery().eq(Patient::getId, id));
    return Result.success(patient);
}

3.使用AopContext.currentProxy()获取当前对象的代理对象,调用代理对象内的方法

@Override
public Result<List<Patient>> getPatients() {
    PatientService proxy = (PatientService) AopContext.currentProxy();
    Patient patient1 = proxy.getPatientById(1).getData();
    Patient patient2 = proxy.getPatientById(2).getData();
    return Result.success(List.of(patient1, patient2));
}

@Override
@Transactional
public Result<Patient> getPatientById(Integer id) {
    Patient patient = patientMapper.selectOne(Wrappers.<Patient>lambdaQuery().eq(Patient::getId, id));
    return Result.success(patient);
}