<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实现主从的方式,具体的使用请参考官方文档或关注之后的推文。
下面的方式与注解的配置作用是相同的:
@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动态代理,与标签的使用方式类似,主要分为两种:
/** * {@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。
/** * {@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"> * @Bean * @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"> * @Bean * public PlatformTransactionManager txManager() { * return new DataSourceTransactionManager(dataSource()); * } * * @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; } }
参考:http://www.lanxinbase.com/?p=1777
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; }
}
}
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接管事务管理。
@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。