关于spring和springboot的transactionManager那些事

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

  • 通过配置文件的方式:

      1. `<master-slave:data-source id="shardingDataSource"`
2. `master-data-source-name="userDataSource" slave-data-source-names="userDataSourceSlave"`
3. `strategy-ref="randomStrategy" />`
4. 
5. `<!-- 配置MyBatis session工厂 -->`
6. `<bean id="userSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">`
7. `<property name="dataSource" ref="shardingDataSource" />`
8. `<property name="configLocation" value="classpath:/conf/mybaits-config.xml" />`
9. `<property name="mapperLocations">`
10. `<list>`
11. `<value>classpath:/**/dao/mapper/*Mapper.xml</value>`
12. `</list>`
13. `</property>`
14. `<property name="plugins">`
15. `<array>`
16. `<bean class="com.github.pagehelper.PageInterceptor">`
17. `<property name="properties">`
18. `<value>`
19. `helperDialect=mysql`
20. `reasonable=true`
21. `supportMethodsArguments=true`
22. `params=count=countSql`
23. `autoRuntimeDialect=true`
24. `</value>`
25. `</property>`
26. `</bean>`
27. `</array>`
28. `</property>`
29. 
30. `</bean>`
31. 
32. `<bean id="userSqlSession" class="org.mybatis.spring.SqlSessionTemplate">`
33. `<constructor-arg index="0">`
34. `<ref bean="userSqlSessionFactory" />`
35. `</constructor-arg>`
36. `</bean>`
37. 
38. `<!--动态代理实现 不用写dao的实现 -->`
39. `<bean id="userMapperScannerConfigurer" class="tk.mybatis.spring.mapper.MapperScannerConfigurer">`
40. `<!-- 这里的basePackage 指定了dao层接口路劲,这里的dao接口不用自己实现 -->`
41. `<property name="basePackage" value="com.**.dao.mapper" />`
42. `<!-- 如果只有一个数据源的话可以不用指定,但是如果有多个数据源的话必须要指定 -->`
43. `<property name="sqlSessionFactoryBeanName" value="userSqlSessionFactory"/>`
44. `<!--直接指定了sqlsessionTemplate名称,这个和上面的其实是一样的 -->`
45. `<!-- <property name="sqlSessionTemplateBeanName" value="userSqlSession" /> -->`
46. `</bean>`
47. 
48. `<!--事务管理器 -->`
49. `<bean id="txUserManager"`
50. `class="org.springframework.jdbc.datasource.DataSourceTransactionManager">`
51. `<property name="dataSource" ref="shardingDataSource" />`
52. `</bean>`
53. 
54. `<!-- 只对业务逻辑层实施事务 -->`
55. `<aop:config expose-proxy="true">`
56. 
57. 
58. `<aop:pointcut id="txUserPointcut"`
59. `expression="execution(* com..dao..*.*(..)) " />`
60. `<aop:advisor advice-ref="txUserAdvice" pointcut-ref="txUserPointcut" />`
61. `</aop:config>`
62. 
63. `<tx:advice id="txUserAdvice" transaction-manager="txUserManager">`
64. `<tx:attributes>`
65. `<tx:method name="create*" propagation="REQUIRED"`
66. `rollback-for="Exception" />`
67. `<tx:method name="insert*" propagation="REQUIRED"`
68. `rollback-for="Exception" />`
69. `<tx:method name="delete*" propagation="REQUIRED"`
70. `rollback-for="Exception" />`
71. `<tx:method name="update*" propagation="REQUIRED"`
72. `rollback-for="Exception" />`
73. `<tx:method name="query*" read-only="true" />`
74. `<tx:method name="count*" read-only="true" />`
75. `<tx:method name="select*" read-only="true" />`
76. `<tx:method name="*" propagation="REQUIRED" />`
77. `</tx:attributes>`
78. `</tx:advice>`


    
  • 注解的方式:

      1. `<master-slave:data-source id="shardingDataSource"`
2. `master-data-source-name="userDataSource" slave-data-source-names="userDataSourceSlave"`
3. `strategy-ref="randomStrategy" />`
4. 
5. `<bean id="userSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">`
6. `<property name="dataSource" ref="shardingDataSource" />`
7. `<property name="configLocation" value="classpath:/conf/mybaits-config.xml" />`
8. `<property name="mapperLocations">`
9. `<list>`
10. `<value>classpath:/**/dao/mapper/*Mapper.xml</value>`
11. `</list>`
12. `</property>`
13. `<property name="plugins">`
14. `<array>`
15. `<bean class="com.github.pagehelper.PageInterceptor">`
16. `<property name="properties">`
17. `<value>`
18. `helperDialect=mysql`
19. `reasonable=true`
20. `supportMethodsArguments=true`
21. `params=count=countSql`
22. `autoRuntimeDialect=true`
23. `</value>`
24. `</property>`
25. `</bean>`
26. `</array>`
27. `</property>`
28. 
29. `</bean>`
30. 
31. `<bean id="userSqlSession" class="org.mybatis.spring.SqlSessionTemplate">`
32. `<constructor-arg index="0">`
33. `<ref bean="userSqlSessionFactory" />`
34. `</constructor-arg>`
35. `</bean>`
36. 
37. `<!--动态代理实现 不用写dao的实现 -->`
38. `<bean id="userMapperScannerConfigurer" class="tk.mybatis.spring.mapper.MapperScannerConfigurer">`
39. `<!-- 这里的basePackage 指定了dao层接口路劲,这里的dao接口不用自己实现 -->`
40. `<property name="basePackage" value="com.**.dao.mapper" />`
41. `<!-- 如果只有一个数据源的话可以不用指定,但是如果有多个数据源的话必须要指定 -->`
42. `<property name="sqlSessionFactoryBeanName" value="userSqlSessionFactory"/>`
43. `<!--直接指定了sqlsessionTemplate名称,这个和上面的其实是一样的 -->`
44. `<!-- <property name="sqlSessionTemplateBeanName" value="userSqlSession" /> -->`
45. `</bean>`
46. 
47. `<!--事务管理器 -->`
48. `<bean id="txUserManager"`
49. `class="org.springframework.jdbc.datasource.DataSourceTransactionManager">`
50. `<property name="dataSource" ref="shardingDataSource" />`
51. `</bean>`
52. `<!-- 使用全注释事务 -->`
53. `<tx:annotation-driven transaction-manager="txUserManager" />`


    

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

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

  1. springboot中使用的方式:

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


      1. `@Configuration`
2. `@EnableTransactionManagement`
3. `public class AppConfig {`
4. `@Bean`
5. `public FooRepository fooRepository() {`
6. `// configure and return a class having @Transactional methods`
7. `return new JdbcFooRepository(dataSource());`
8. `}`
9. 
10. `@Bean`
11. `public DataSource dataSource() {`
12. `// configure and return the necessary JDBC DataSource`
13. `}`
14. 
15. `@Bean`
16. `public PlatformTransactionManager txManager() {`
17. `return new DataSourceTransactionManager(dataSource());`
18. `}`
19. `}`


    

看下@EnableTransactionManagement注解:


      1. `@Target(ElementType.TYPE)`
2. `@Retention(RetentionPolicy.RUNTIME)`
3. `@Documented`
4. `@Import(TransactionManagementConfigurationSelector.class)`
5. `public @interface EnableTransactionManagement {`
6. `boolean proxyTargetClass() default false;`
7. `AdviceMode mode() default AdviceMode.PROXY;`


    

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

  • 默认使用的是org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration:

      1. `/**`
2. `* {@code @Configuration} class that registers the Spring infrastructure beans`
3. `* necessary to enable proxy-based annotation-driven transaction management.`
4. `*`
5. `* @author Chris Beams`
6. `* @since 3.1`
7. `* @see EnableTransactionManagement`
8. `* @see TransactionManagementConfigurationSelector`
9. `*/`
10. `@Configuration`
11. `public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {`
12. 
13. `@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)`
14. `@Role(BeanDefinition.ROLE_INFRASTRUCTURE)`
15. `public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {`
16. `// 创建BeanFactoryTransactionAttributeSourceAdvisor`
17. `BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();`
18. `advisor.setTransactionAttributeSource(transactionAttributeSource());`
19. `//设置事务拦截器`
20. `advisor.setAdvice(transactionInterceptor());`
21. `advisor.setOrder(this.enableTx.<Integer>getNumber("order"));`
22. `return advisor;`
23. `}`
24. 
25. `@Bean`
26. `@Role(BeanDefinition.ROLE_INFRASTRUCTURE)`
27. `public TransactionAttributeSource transactionAttributeSource() {`
28. `return new AnnotationTransactionAttributeSource();`
29. `}`
30. 
31. `@Bean`
32. `@Role(BeanDefinition.ROLE_INFRASTRUCTURE)`
33. `public TransactionInterceptor transactionInterceptor() {`
34. `TransactionInterceptor interceptor = new TransactionInterceptor();`
35. `interceptor.setTransactionAttributeSource(transactionAttributeSource());`
36. `if (this.txManager != null) {`
37. `//设置事务管理器`
38. `interceptor.setTransactionManager(this.txManager);`
39. `}`
40. `return interceptor;`
41. `}`
42. 
43. `}`


    

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

  • aspectj代理的方式org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration:

      1. `/**`
2. `* {@code @Configuration} class that registers the Spring infrastructure beans necessary`
3. `* to enable AspectJ-based annotation-driven transaction management.`
4. `*`
5. `* @author Chris Beams`
6. `* @since 3.1`
7. `* @see EnableTransactionManagement`
8. `* @see TransactionManagementConfigurationSelector`
9. `*/`
10. `@Configuration`
11. `public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {`
12. 
13. `@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)`
14. `@Role(BeanDefinition.ROLE_INFRASTRUCTURE)`
15. `public AnnotationTransactionAspect transactionAspect() {`
16. `AnnotationTransactionAspect txAspect = AnnotationTransactionAspect.aspectOf();`
17. `if (this.txManager != null) {`
18. `txAspect.setTransactionManager(this.txManager);`
19. `}`
20. `return txAspect;`
21. `}`
22. 
23. `}`


    

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

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


      1. `/**`
2. `* Return the default transaction manager bean to use for annotation-driven database`
3. `* transaction management, i.e. when processing {@code @Transactional} methods.`
4. `* <p>There are two basic approaches to implementing this method:`
5. `* <h3>1. Implement the method and annotate it with {@code @Bean}</h3>`
6. `* In this case, the implementing {@code @Configuration} class implements this method,`
7. `* marks it with {@code @Bean} and configures and returns the transaction manager`
8. `* directly within the method body:`
9. `* <pre class="code">`
10. `* @Bean`
11. `* @Override`
12. `* public PlatformTransactionManager annotationDrivenTransactionManager() {`
13. `* return new DataSourceTransactionManager(dataSource());`
14. `* }</pre>`
15. `* <h3>2. Implement the method without {@code @Bean} and delegate to another existing`
16. `* {@code @Bean} method</h3>`
17. `* <pre class="code">`
18. `* @Bean`
19. `* public PlatformTransactionManager txManager() {`
20. `* return new DataSourceTransactionManager(dataSource());`
21. `* }`
22. `*`
23. `* @Override`
24. `* public PlatformTransactionManager annotationDrivenTransactionManager() {`
25. `* return txManager(); // reference the existing {@code @Bean} method above`
26. `* }</pre>`
27. `* If taking approach #2, be sure that <em>only one</em> of the methods is marked`
28. `* with {@code @Bean}!`
29. `* <p>In either scenario #1 or #2, it is important that the`
30. `* {@code PlatformTransactionManager} instance is managed as a Spring bean within the`
31. `* container as all {@code PlatformTransactionManager} implementations take advantage`
32. `* of Spring lifecycle callbacks such as {@code InitializingBean} and`
33. `* {@code BeanFactoryAware}.`
34. `*/`
35. `PlatformTransactionManager annotationDrivenTransactionManager();`


    

实现方式为:


      1. `@Configuration`
2. `@EnableTransactionManagement`
3. `public class AppConfig implements TransactionManagementConfigurer {`
4. 
5. `@Bean`
6. `public DataSource dataSource() {`
7. `// configure and return the necessary JDBC DataSource`
8. `}`
9. 
10. `@Bean`
11. `public PlatformTransactionManager txManager() {`
12. `return new DataSourceTransactionManager(dataSource());`
13. `}`
14. 
15. `@Override`
16. `public PlatformTransactionManager annotationDrivenTransactionManager() {`
17. `return txManager();`
18. `}`
19. `}`


    

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

picture.image

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


      1. `@Bean`
2. `@Role(BeanDefinition.ROLE_INFRASTRUCTURE)`
3. `public TransactionInterceptor transactionInterceptor() {`
4. `TransactionInterceptor interceptor = new TransactionInterceptor();`
5. `interceptor.setTransactionAttributeSource(transactionAttributeSource());`
6. `if (this.txManager != null) {`
7. `interceptor.setTransactionManager(this.txManager);`
8. `}`
9. `return interceptor;`
10. `}`


    

它父类中的实现为:


      1. `它的父类的签名为:`
2. `public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean`
3. 
4. `/**`
5. `* Return the BeanFactory to use for retrieving PlatformTransactionManager beans.`
6. `*/`
7. `protected final BeanFactory getBeanFactory() {//注入beanFactory来获取PlatformTransactionManager实例`
8. `return this.beanFactory;`
9. `}`
10. 
11. `/**`
12. `* Determine the specific transaction manager to use for the given transaction.`
13. `*/`
14. `protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {`
15. `// Do not attempt to lookup tx manager if no tx attributes are set`
16. `if (txAttr == null || this.beanFactory == null) {`
17. `return getTransactionManager();`
18. `}`
19. `String qualifier = txAttr.getQualifier();`
20. `if (StringUtils.hasText(qualifier)) {`
21. `return determineQualifiedTransactionManager(qualifier);`
22. `}`
23. `else if (StringUtils.hasText(this.transactionManagerBeanName)) {`
24. `return determineQualifiedTransactionManager(this.transactionManagerBeanName);`
25. `}`
26. `else {`
27. `PlatformTransactionManager defaultTransactionManager = getTransactionManager();`
28. `if (defaultTransactionManager == null) {`
29. `defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);`
30. `if (defaultTransactionManager == null) {`
31. `defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);`
32. `this.transactionManagerCache.putIfAbsent(`
33. `DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);`
34. `}`
35. `}`
36. `return defaultTransactionManager;`
37. `}`
38. `}`


    
  • 添加TransactionInterceptor,保存了事务信息,并且是一个MethodInterceptor,会拦截带有@Transactional的方法。先获取事务相关的信息,然后获取PlatformTransactionManager
  • 见org.springframework.transaction.interceptor.TransactionAspectSupport#determineTransactionManager,如果事先没有指定transactionManager,则会通过beanFactory从容器中获取一个。

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

  1. springboot注入transactionManager的方式为:

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


      1. `/**`
2. `* {@link EnableAutoConfiguration Auto-configuration} for`
3. `* {@link DataSourceTransactionManager}.`
4. `*`
5. `* @author Dave Syer`
6. `* @author Stephane Nicoll`
7. `* @author Andy Wilkinson`
8. `* @author Kazuki Shimizu`
9. `*/`
10. `@Configuration`
11. `@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })`
12. `@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)`
13. `@EnableConfigurationProperties(DataSourceProperties.class)`
14. `public class DataSourceTransactionManagerAutoConfiguration {`
15. 
16. `@Configuration`
17. `@ConditionalOnSingleCandidate(DataSource.class)`
18. `static class DataSourceTransactionManagerConfiguration {`
19. 
20. `private final DataSource dataSource;`
21. 
22. `private final TransactionManagerCustomizers transactionManagerCustomizers;`
23. 
24. `DataSourceTransactionManagerConfiguration(DataSource dataSource,`
25. `ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {`
26. `this.dataSource = dataSource;`
27. `this.transactionManagerCustomizers = transactionManagerCustomizers`
28. `.getIfAvailable();`
29. `}`
30. 
31. `@Bean`
32. `@ConditionalOnMissingBean(PlatformTransactionManager.class)`
33. `public DataSourceTransactionManager transactionManager(`
34. `DataSourceProperties properties) {`
35. `DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(`
36. `this.dataSource);`
37. `if (this.transactionManagerCustomizers != null) {`
38. `this.transactionManagerCustomizers.customize(transactionManager);`
39. `}`
40. `return transactionManager;`
41. `}`
42. 
43. `}`
44. 
45. `}`


    
  • 如果没有指定transactionManager时,使用springboot时会自动配置PlatformTransactionManager。
  • springboot项目中会使用这个transactionManager来进行事务的管理(如果没有使用@Transactional注解,springboot则不会进行事务管理)。
  • 有了autoConfiguration,@EnableTransactionManagement注解可加可不加,如果需要修改代理模式时最好加上。
  • 关于PlatformTransactionManager:

picture.image

  • 关于DataSourceTransactionManager中事务的提交设置,可以看看doBegin方法:

      1. `protected void doBegin(Object transaction, TransactionDefinition definition) {`
2. `DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;`
3. `Connection con = null;`
4. 
5. `try {`
6. `if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {`
7. `Connection newCon = this.dataSource.getConnection();`
8. `if (this.logger.isDebugEnabled()) {`
9. `this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");`
10. `}`
11. 
12. `txObject.setConnectionHolder(new ConnectionHolder(newCon), true);`
13. `}`
14. 
15. `txObject.getConnectionHolder().setSynchronizedWithTransaction(true);`
16. `con = txObject.getConnectionHolder().getConnection();`
17. `Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);`
18. `txObject.setPreviousIsolationLevel(previousIsolationLevel);`
19. `if (con.getAutoCommit()) {`
20. `txObject.setMustRestoreAutoCommit(true);`
21. `if (this.logger.isDebugEnabled()) {`
22. `this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");`
23. `}`
24. 
25. `con.setAutoCommit(false);`
26. `}`
27. `--------------------------省略部分代码------------------------`
28. `}`


    

主要看下这段代码:


      1. `if (con.getAutoCommit()) {`
2. `txObject.setMustRestoreAutoCommit(true);`
3. `if (this.logger.isDebugEnabled()) {`
4. `this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");`
5. `}`
6. 
7. `con.setAutoCommit(false);`
8. `}`


    

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

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

  • 自动配置的类:tk.mybatis.mapper.autoconfigure.MapperAutoConfiguration

      1. `@Bean`
2. `@ConditionalOnMissingBean`
3. `public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {`
4. `SqlSessionFactoryBean factory = new SqlSessionFactoryBean();`
5. `------------省略部分代码-------------`
6. `return factory.getObject();`
7. `}`
8. 
9. `@Bean`
10. `@ConditionalOnMissingBean`
11. `public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {`
12. `ExecutorType executorType = this.properties.getExecutorType();`
13. `if (executorType != null) {`
14. `return new SqlSessionTemplate(sqlSessionFactory, executorType);`
15. `} else {`
16. `return new SqlSessionTemplate(sqlSessionFactory);`
17. `}`
18. `}`


    

获取datasource的方式为:


      1. `public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {`
2. `this(sqlSessionFactory, executorType,`
3. `new MyBatisExceptionTranslator(`
4. `sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));`
5. `}`


    

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

  • 扫描mapper文件的类为:tk.mybatis.spring.annotation.MapperScannerRegistrar

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
大规模高性能计算集群优化实践
随着机器学习的发展,数据量和训练模型都有越来越大的趋势,这对基础设施有了更高的要求,包括硬件、网络架构等。本次分享主要介绍火山引擎支撑大规模高性能计算集群的架构和优化实践。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论