前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于spring和springboot的transactionManager那些事

关于spring和springboot的transactionManager那些事

作者头像
山行AI
发布2019-07-12 15:18:09
15.6K0
发布2019-07-12 15:18:09
举报
文章被收录于专栏:山行AI山行AI山行AI

1. 传统的spring事务管理与myibatis整合方式

  • 通过配置文件的方式:
<master-slave:data-source id="shardingDataSource"        master-data-source-name="userDataSource" slave-data-source-names="userDataSourceSlave"        strategy-ref="randomStrategy" />
<!-- 配置MyBatis session工厂 -->    <bean id="userSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">        <property name="dataSource" ref="shardingDataSource" />        <property name="configLocation" value="classpath:/conf/mybaits-config.xml" />        <property name="mapperLocations">            <list>                <value>classpath:/**/dao/mapper/*Mapper.xml</value>            </list>        </property>        <property name="plugins">            <array>                <bean class="com.github.pagehelper.PageInterceptor">                    <property name="properties">                        <value>                            helperDialect=mysql                            reasonable=true                            supportMethodsArguments=true                            params=count=countSql                            autoRuntimeDialect=true                        </value>                    </property>                </bean>            </array>        </property>
    </bean>
    <bean id="userSqlSession" class="org.mybatis.spring.SqlSessionTemplate">          <constructor-arg index="0">              <ref bean="userSqlSessionFactory" />          </constructor-arg>      </bean>  
    <!--动态代理实现 不用写dao的实现 -->    <bean id="userMapperScannerConfigurer" class="tk.mybatis.spring.mapper.MapperScannerConfigurer">        <!-- 这里的basePackage 指定了dao层接口路劲,这里的dao接口不用自己实现 -->        <property name="basePackage" value="com.**.dao.mapper" />        <!-- 如果只有一个数据源的话可以不用指定,但是如果有多个数据源的话必须要指定 -->        <property name="sqlSessionFactoryBeanName" value="userSqlSessionFactory"/>        <!--直接指定了sqlsessionTemplate名称,这个和上面的其实是一样的 -->        <!-- <property name="sqlSessionTemplateBeanName" value="userSqlSession" /> -->    </bean>
    <!--事务管理器 -->    <bean id="txUserManager"        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="shardingDataSource" />    </bean>
    <!-- 只对业务逻辑层实施事务 -->    <aop:config expose-proxy="true">

        <aop:pointcut id="txUserPointcut"            expression="execution(* com..dao..*.*(..)) " />        <aop:advisor advice-ref="txUserAdvice" pointcut-ref="txUserPointcut" />    </aop:config>
    <tx:advice id="txUserAdvice" transaction-manager="txUserManager">        <tx:attributes>            <tx:method name="create*" propagation="REQUIRED"                rollback-for="Exception" />            <tx:method name="insert*" propagation="REQUIRED"                rollback-for="Exception" />            <tx:method name="delete*" propagation="REQUIRED"                rollback-for="Exception" />            <tx:method name="update*" propagation="REQUIRED"                rollback-for="Exception" />            <tx:method name="query*" read-only="true" />            <tx:method name="count*" read-only="true" />            <tx:method name="select*" read-only="true" />            <tx:method name="*" propagation="REQUIRED" />        </tx:attributes>    </tx:advice>
  • 注解的方式:
    <master-slave:data-source id="shardingDataSource"            master-data-source-name="userDataSource" slave-data-source-names="userDataSourceSlave"            strategy-ref="randomStrategy" />
    <bean id="userSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">            <property name="dataSource" ref="shardingDataSource" />            <property name="configLocation" value="classpath:/conf/mybaits-config.xml" />            <property name="mapperLocations">                <list>                    <value>classpath:/**/dao/mapper/*Mapper.xml</value>                </list>            </property>            <property name="plugins">                <array>                    <bean class="com.github.pagehelper.PageInterceptor">                        <property name="properties">                            <value>                                helperDialect=mysql                                reasonable=true                                supportMethodsArguments=true                                params=count=countSql                                autoRuntimeDialect=true                            </value>                        </property>                    </bean>                </array>            </property>
        </bean>
        <bean id="userSqlSession" class="org.mybatis.spring.SqlSessionTemplate">              <constructor-arg index="0">                  <ref bean="userSqlSessionFactory" />              </constructor-arg>          </bean>  
        <!--动态代理实现 不用写dao的实现 -->        <bean id="userMapperScannerConfigurer" class="tk.mybatis.spring.mapper.MapperScannerConfigurer">            <!-- 这里的basePackage 指定了dao层接口路劲,这里的dao接口不用自己实现 -->            <property name="basePackage" value="com.**.dao.mapper" />            <!-- 如果只有一个数据源的话可以不用指定,但是如果有多个数据源的话必须要指定 -->            <property name="sqlSessionFactoryBeanName" value="userSqlSessionFactory"/>            <!--直接指定了sqlsessionTemplate名称,这个和上面的其实是一样的 -->            <!-- <property name="sqlSessionTemplateBeanName" value="userSqlSession" /> -->        </bean>
        <!--事务管理器 -->        <bean id="txUserManager"            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">            <property name="dataSource" ref="shardingDataSource" />        </bean>    <!-- 使用全注释事务 -->    <tx:annotation-driven transaction-manager="txUserManager" /> 

注解的方式主要是使用tx:annotation-driven实现的,这样在代码里的方法上或类上就可以使用@Transactional注解来灵活地控制事务了。

这里的dataSource使用的是shardingjdbcDatasource实现主从的方式,具体的使用请参考官方文档或关注之后的推文。

2. springboot中使用的方式:

下面的方式与注解的配置作用是相同的:

@Configuration @EnableTransactionManagement public class AppConfig {     @Bean     public FooRepository fooRepository() {         // configure and return a class having @Transactional methods         return new JdbcFooRepository(dataSource());     }
     @Bean     public DataSource dataSource() {         // configure and return the necessary JDBC DataSource     }
     @Bean     public PlatformTransactionManager txManager() {         return new DataSourceTransactionManager(dataSource());     } }

看下@EnableTransactionManagement注解:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(TransactionManagementConfigurationSelector.class)public @interface EnableTransactionManagement {boolean proxyTargetClass() default false;AdviceMode mode() default AdviceMode.PROXY;

可以看到有个AdviceMode,代表的是代理的模式,默认使用的是jdk动态代理,与标签的使用方式类似,主要分为两种:

  • 默认使用的是org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration:
/** * {@code @Configuration} class that registers the Spring infrastructure beans * necessary to enable proxy-based annotation-driven transaction management. * * @author Chris Beams * @since 3.1 * @see EnableTransactionManagement * @see TransactionManagementConfigurationSelector */@Configurationpublic class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {    //    创建BeanFactoryTransactionAttributeSourceAdvisor        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();        advisor.setTransactionAttributeSource(transactionAttributeSource());        //设置事务拦截器        advisor.setAdvice(transactionInterceptor());        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));        return advisor;    }
    @Bean    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)    public TransactionAttributeSource transactionAttributeSource() {        return new AnnotationTransactionAttributeSource();    }
    @Bean    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)    public TransactionInterceptor transactionInterceptor() {        TransactionInterceptor interceptor = new TransactionInterceptor();        interceptor.setTransactionAttributeSource(transactionAttributeSource());        if (this.txManager != null) {        //设置事务管理器            interceptor.setTransactionManager(this.txManager);        }        return interceptor;    }
}

通过类的注释可以知道,这个类的作用是enable proxy-based annotation-driven transaction management。

  • aspectj代理的方式org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration:
/** * {@code @Configuration} class that registers the Spring infrastructure beans necessary * to enable AspectJ-based annotation-driven transaction management. * * @author Chris Beams * @since 3.1 * @see EnableTransactionManagement * @see TransactionManagementConfigurationSelector */@Configurationpublic class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)    public AnnotationTransactionAspect transactionAspect() {        AnnotationTransactionAspect txAspect = AnnotationTransactionAspect.aspectOf();        if (this.txManager != null) {            txAspect.setTransactionManager(this.txManager);        }        return txAspect;    }
}

上面两种方式都可以进行事务管理的注册,但是基于标签的方式需要传入一个指定的txManager,因为它没法自行找到一个叫txManager的bean。但是@EnableTransactionManagement 注解的方式就灵活很多了,它可以通过类型寻找到容器内的任何的基于PlatformTransactionManager实现的bean。

如果想要将@EnableTransactionManagement和transaction manager之间建立直接的联系,需要用到org.springframework.transaction.annotation.TransactionManagementConfigurer的回调方法:

/**     * Return the default transaction manager bean to use for annotation-driven database     * transaction management, i.e. when processing {@code @Transactional} methods.     * <p>There are two basic approaches to implementing this method:     * <h3>1. Implement the method and annotate it with {@code @Bean}</h3>     * In this case, the implementing {@code @Configuration} class implements this method,     * marks it with {@code @Bean} and configures and returns the transaction manager     * directly within the method body:     * <pre class="code">     * &#064;Bean     * &#064;Override     * public PlatformTransactionManager annotationDrivenTransactionManager() {     *     return new DataSourceTransactionManager(dataSource());     * }</pre>     * <h3>2. Implement the method without {@code @Bean} and delegate to another existing     * {@code @Bean} method</h3>     * <pre class="code">     * &#064;Bean     * public PlatformTransactionManager txManager() {     *     return new DataSourceTransactionManager(dataSource());     * }     *     * &#064;Override     * public PlatformTransactionManager annotationDrivenTransactionManager() {     *     return txManager(); // reference the existing {@code @Bean} method above     * }</pre>     * If taking approach #2, be sure that <em>only one</em> of the methods is marked     * with {@code @Bean}!     * <p>In either scenario #1 or #2, it is important that the     * {@code PlatformTransactionManager} instance is managed as a Spring bean within the     * container as all {@code PlatformTransactionManager} implementations take advantage     * of Spring lifecycle callbacks such as {@code InitializingBean} and     * {@code BeanFactoryAware}.     */    PlatformTransactionManager annotationDrivenTransactionManager();

实现方式为:

@Configuration @EnableTransactionManagement public class AppConfig implements TransactionManagementConfigurer {
     @Bean     public DataSource dataSource() {         // configure and return the necessary JDBC DataSource     }
     @Bean     public PlatformTransactionManager txManager() {         return new DataSourceTransactionManager(dataSource());     }
     @Override     public PlatformTransactionManager annotationDrivenTransactionManager() {         return txManager();     } }

这个方法会在设置transactionManager时被调用,可以看下ProxyTransactionManagementConfiguration和AspectJTransactionManagementConfiguration的父类中的方法:

这时再回过头来看org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration#transactionInterceptor:

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

它父类中的实现为:

它的父类的签名为:public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean
        /**         * Return the BeanFactory to use for retrieving PlatformTransactionManager beans.         */        protected final BeanFactory getBeanFactory() {//注入beanFactory来获取PlatformTransactionManager实例            return this.beanFactory;        }
        /**         * Determine the specific transaction manager to use for the given transaction.         */        protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {            // Do not attempt to lookup tx manager if no tx attributes are set            if (txAttr == null || this.beanFactory == null) {                return getTransactionManager();            }            String qualifier = txAttr.getQualifier();            if (StringUtils.hasText(qualifier)) {                return determineQualifiedTransactionManager(qualifier);            }            else if (StringUtils.hasText(this.transactionManagerBeanName)) {                return determineQualifiedTransactionManager(this.transactionManagerBeanName);            }            else {                PlatformTransactionManager defaultTransactionManager = getTransactionManager();                if (defaultTransactionManager == null) {                    defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);                    if (defaultTransactionManager == null) {                        defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);                        this.transactionManagerCache.putIfAbsent(                                DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);                    }                }                return defaultTransactionManager;            }        }
  • 添加TransactionInterceptor,保存了事务信息,并且是一个MethodInterceptor,会拦截带有@Transactional的方法。先获取事务相关的信息,然后获取PlatformTransactionManager
  • 见org.springframework.transaction.interceptor.TransactionAspectSupport#determineTransactionManager,如果事先没有指定transactionManager,则会通过beanFactory从容器中获取一个。

参考:http://www.lanxinbase.com/?p=1777

3. springboot注入transactionManager的方式为:

org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration:

/** * {@link EnableAutoConfiguration Auto-configuration} for * {@link DataSourceTransactionManager}. * * @author Dave Syer * @author Stephane Nicoll * @author Andy Wilkinson * @author Kazuki Shimizu */@Configuration@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)@EnableConfigurationProperties(DataSourceProperties.class)public class DataSourceTransactionManagerAutoConfiguration {
    @Configuration    @ConditionalOnSingleCandidate(DataSource.class)    static class DataSourceTransactionManagerConfiguration {
        private final DataSource dataSource;
        private final TransactionManagerCustomizers transactionManagerCustomizers;
        DataSourceTransactionManagerConfiguration(DataSource dataSource,                ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {            this.dataSource = dataSource;            this.transactionManagerCustomizers = transactionManagerCustomizers                    .getIfAvailable();        }
        @Bean        @ConditionalOnMissingBean(PlatformTransactionManager.class)        public DataSourceTransactionManager transactionManager(                DataSourceProperties properties) {            DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(                    this.dataSource);            if (this.transactionManagerCustomizers != null) {                this.transactionManagerCustomizers.customize(transactionManager);            }            return transactionManager;        }
    }
}
  • 如果没有指定transactionManager时,使用springboot时会自动配置PlatformTransactionManager。
  • springboot项目中会使用这个transactionManager来进行事务的管理(如果没有使用@Transactional注解,springboot则不会进行事务管理)。
  • 有了autoConfiguration,@EnableTransactionManagement注解可加可不加,如果需要修改代理模式时最好加上。
  • 关于PlatformTransactionManager:
  • 关于DataSourceTransactionManager中事务的提交设置,可以看看doBegin方法:
protected void doBegin(Object transaction, TransactionDefinition definition) {        DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;        Connection con = null;
        try {            if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {                Connection newCon = this.dataSource.getConnection();                if (this.logger.isDebugEnabled()) {                    this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");                }
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);            }
            txObject.getConnectionHolder().setSynchronizedWithTransaction(true);            con = txObject.getConnectionHolder().getConnection();            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);            txObject.setPreviousIsolationLevel(previousIsolationLevel);            if (con.getAutoCommit()) {                txObject.setMustRestoreAutoCommit(true);                if (this.logger.isDebugEnabled()) {                    this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");                }
                con.setAutoCommit(false);            }        --------------------------省略部分代码------------------------    }

主要看下这段代码:

 if (con.getAutoCommit()) {                txObject.setMustRestoreAutoCommit(true);                if (this.logger.isDebugEnabled()) {                    this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");                }
                con.setAutoCommit(false);            }

也就是说,当把事务管理交给spring之后,不管在mysql server中事务是设置成手动提交还是自动提交的,这里都会被设置成手动提交,然后由spring接管事务管理。

4. 附:tk myibatis插件注入datasource的方式

  • 自动配置的类:tk.mybatis.mapper.autoconfigure.MapperAutoConfiguration
 @Bean    @ConditionalOnMissingBean    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();           ------------省略部分代码-------------        return factory.getObject();    }
    @Bean    @ConditionalOnMissingBean    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {        ExecutorType executorType = this.properties.getExecutorType();        if (executorType != null) {            return new SqlSessionTemplate(sqlSessionFactory, executorType);        } else {            return new SqlSessionTemplate(sqlSessionFactory);        }    }

获取datasource的方式为:

 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {    this(sqlSessionFactory, executorType,        new MyBatisExceptionTranslator(            sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));  }

sqlSessionFactory.getConfiguration().getEnvironment().getDataSource()获取到的是当前环境中使用的datasource。

  • 扫描mapper文件的类为:tk.mybatis.spring.annotation.MapperScannerRegistrar
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 传统的spring事务管理与myibatis整合方式
  • 2. springboot中使用的方式:
  • 3. springboot注入transactionManager的方式为:
  • 4. 附:tk myibatis插件注入datasource的方式
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档