@Transactional

简介:

1. @EnableTransactionManagement

正常来讲,springboot要使用注解方式的声明式事务,需要在启动类加上@EnableTransactionManagement,表示启注解事务管理,等同于xml配置方式的<tx:annotation-driven />

但是springboot的自动装配会自动配置事务,在自动配置类里启用了@EnableTransactionManagement,开发者只需要在需要事务的方法和类上使用@Transactional注解来控制事务就行了。

那么,springboot的EnableTransactionManagement这注解是不是多余的呢?

在自动装配下,@EnableTransactionManagement确实是多余的,但是重复配置也没有问题,且默认使用PROXY方式,即jdk动态代理方式。

需要注意的是,@EnableTransactionManagementspring-tx的注解,不是spring-boot的,spring-boot会自动配置事务,相关的配置在org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({PlatformTransactionManager.class})
@AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
@EnableConfigurationProperties({TransactionProperties.class})
public class TransactionAutoConfiguration {
    ...
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnBean({TransactionManager.class})
    @ConditionalOnMissingBean({AbstractTransactionManagementConfiguration.class})
    public static class EnableTransactionManagementConfiguration {
        ...
        @Configuration(proxyBeanMethods = false)
        @EnableTransactionManagement(proxyTargetClass = false)
        @ConditionalOnProperty(prefix = "spring.aop",name = {"proxy-target-class"},havingValue = "false",matchIfMissing = false)
        public static class JdkDynamicAutoProxyConfiguration {...}
    }
    ...
}

1.1 @EnableTransactionManagement原理

springboot的通用@EnableXXX原理:注解上有个XXXRegistrar,或通过XXXSelector引入XXXRegistrarXXXRegistrar实现了ImportBeanDefinitionRegistrarregisterBeanDefinitions方法,给容器注册XXXCreator。这个Creator实现了后置处理器,后置处理器在对象创建以后,包装对象,返回一个代理对象,代理对象执行方法利用拦截器链进行调用。

@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 2147483647;
}

@EnableTransactionManagement引入了TransactionManagementConfigurationSelector:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    ...
    protected String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{this.determineTransactionAspectClass()};
        default:
            return null;
        }
    }

    private String determineTransactionAspectClass() {...}
}

在PROXY模式下,导入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration,其中AutoProxyRegistrar:

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    private final Log logger = LogFactory.getLog(this.getClass());
    ...
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean candidateFound = false;
        Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
        Iterator var5 = annTypes.iterator();

        while(var5.hasNext()) {
            String annType = (String)var5.next();
            AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
            if (candidate != null) {
                Object mode = candidate.get("mode");
                Object proxyTargetClass = candidate.get("proxyTargetClass");
                if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) {
                    candidateFound = true;
                    if (mode == AdviceMode.PROXY) {
                        AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                        if ((Boolean)proxyTargetClass) {
                            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                            return;
                        }
                    }
                }
            }
        }

        if (!candidateFound && this.logger.isInfoEnabled()) {
            String name = this.getClass().getSimpleName();
            this.logger.info(String.format("%s was imported but no annotations were found having both 'mode' and 'proxyTargetClass' attributes of type AdviceMode and boolean respectively. This means that auto proxy creator registration and configuration may not have occurred as intended, and components may not be proxied as expected. Check to ensure that %s has been @Import'ed on the same class where these annotations are declared; otherwise remove the import of %s altogether.", name, name, name));
        }
    }
}

AutoProxyRegistrar给容器中注册了一个InfrastructureAdvisorAutoProxyCreator组件,利用后置处理器机制在对象创建以后包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用。

ProxyTransactionManagementConfiguration:

@Configuration(proxyBeanMethods = false)
@Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    ...
    @Bean(name = {"org.springframework.transaction.config.internalTransactionAdvisor"})
    @Role(2)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource);
        advisor.setAdvice(transactionInterceptor);
        if (this.enableTx != null) {
            advisor.setOrder((Integer)this.enableTx.getNumber("order"));
        }

        return advisor;
    }

    @Bean
    @Role(2)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    @Role(2)
    public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource);
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }

        return interceptor;
    }
}

ProxyTransactionManagementConfiguration的作用是给容器中注册事务增强器transactionAdvisor。

transactionAdvisor需要使用事务注解的信息,AnnotationTransactionAttributeSource可以用于解析事务注解。

事务拦截器transactionInterceptor,保存了事务属性、事务管理器信息。TransactionInterceptor实现了MethodInterceptor,在目标方法执行的时候会执行拦截器链,事务拦截器做的工作为:

1)、获取事务相关的属性;

2)、获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager;

3)、执行目标方法,如果发生异常,则利用事务管理回滚操作;如果执行正常,则利用事务管理器,提交事务。

2. @Transactional

@Transactional用于配置需要添加事务的方法的具体事务信息,主要包括:

transactionManager:事务管理器;
propagation:事务的传播行为;
isolation:事务的隔离级别;
readOnly:事务的只读属性;
rollbackFor & noRollbackFor:回滚和不回滚的Throwable配置。

要添加事务,首先要定义切点,@Transactional注解修饰的方法为即为切点。有了切点后,对切点所在类实例进行代理,生成代理对象。代理对象在获取@Transactional注解定义的事务信息后,执行目标方法,目标方法执行正常则提交事务,否则回滚事务。

2.1 @Transactional注意事项

在初用@Transactional时,可能会遇到事务不生效的问题,其原因一般有以下几种情况。

2.1.1、检查你的方法是不是public

@Transactional注解只能应用到public修饰的方法,如果应用在protected、private等方法上不会报错,但是事务不会起作用。

2.1.2、抛出异常类型

Throwable分为errorexceptionexception分为checked和unchecked(受检异常和非受检异常),其中非受检异常仅包括RuntimeException以及它的子类。

image

默认情况下,Spring会对unchecked异常进行事务回滚,如果是checked异常则不回滚。如果你想check异常也回滚怎么办,注解上面写明异常类型即可:

@Transactional(rollbackFor = Exception.class)

类似的,还有norollbackFor,用于自定义不回滚的异常。

2.1.3、数据库引擎

以mysql为例,非innodb引擎,例如myisam本身不支持事务,加了@Transactional也不会生效。

2.1.4、同类中的方法调用

默认只有从类的外部调用被@Transactional注解修饰的方法,事务才会生效。如果从类的内部,例如方法A、B属于同一个类,其中只有B被该注解修饰,当类内部通过A调用B时,事务不会生效。

2.1.5、多数据源配置下

如果项目中有多数据源,如果不是@Primary修饰的主数据源,需要在@Transactional中指定事务管理器,例如:

@Transactional(rollbackFor = Exception.class,value = "msTransactionManager")

或者,设置默认事务管理器,在默认管理器中指定数据源:

/**
 * 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
 * mode用于指定类型,PROXY为java动态代理,ASPECTJ为使用aspectj,默认值为PROXY
 */
@EnableTransactionManagement(mode = AdviceMode.PROXY)
public class PlatformTransactionConfig implements TransactionManagementConfigurer {

    @Resource(name="msTransactionManager")
    private PlatformTransactionManager msTransactionManager;

    // 创建事务管理器1
    @Bean(name = "msTransactionManager")
    public PlatformTransactionManager msTransactionManager(@Qualifier("msDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    // 创建事务管理器2
    @Bean(name = "faithTransactionManager")
    public PlatformTransactionManager faithTransactionManager(@Qualifier("faithDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    // 实现接口 TransactionManagementConfigurer 方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return msTransactionManager;
    }
}

上例中,如果msDataSource就是被@Primary修饰的主数据源,可以将@Qualifier("msDataSource")省略。

此外,@Transactional注解不生效的原因还包括:

添加@Transactional注解方法所在类没有被spring扫描到;

被@Transactional修饰的方法捕获了异常而没有抛出异常,如果只捕获不抛出,事务不会回滚;
目录
相关文章
|
6月前
|
Java 编译器 数据库
@Transactional中指定rollbackFor,弊端以及不能回滚的时候
@Transactional中指定rollbackFor,弊端以及不能回滚的时候
203 3
|
6月前
|
XML Java 关系型数据库
@Transactional注解的失效场景
@Transactional注解的失效场景
107 1
|
6月前
|
Java 编译器 数据库
在事务注解@Transactional中指定rollbackFor
在事务注解@Transactional中指定rollbackFor
58 0
|
1月前
|
监控 Java 数据库
Spring事务中的@Transactional注解剖析
通过上述分析,可以看到 `@Transactional`注解在Spring框架中扮演着关键角色,它简化了事务管理的复杂度,让开发者能够更加专注于业务逻辑本身。合理运用并理解其背后的机制,对于构建稳定、高效的Java企业应用至关重要。
57 0
|
5月前
|
SQL Java 数据库
Transactional注解讲解及使用
事务是数据库操作的一组集合,它作为一个工作单元,要求所有操作要么全部成功,要么全部失败。事务的四个基本特性是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
|
6月前
|
关系型数据库 Java MySQL
一篇文章学会使用@Transactional
一篇文章学会使用@Transactional
75 0
|
SQL Java 数据库连接
@Transactional
@Transactional
108 0
|
Java 数据库 Spring
@Transactional注解超详细
@Transactional注解超详细
890 0
|
Java 数据库 Spring
@Transactional 注解失效问题
@Transactional 注解失效问题
108 0
|
数据库
Transactional注解不生效案例
Transactional注解不生效案例
148 0
Transactional注解不生效案例