背景:项目中使用了两个mysql数据源,有一个功能是同时修改两个库里的表数据,需要进行事务控制。项目框架为springcloud+mybatis。
项目结构如下
增加maven依赖
org.springframework.boot spring-boot-starter-jta-atomikos org.projectlombok lombok
application.properties
#carinfo spring.datasource.carinfo.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.carinfo.driverClassName=com.mysql.jdbc.Driver spring.datasource.carinfo.url=xxx spring.datasource.carinfo.username=xxx spring.datasource.carinfo.password=xxx spring.datasource.carinfo.maxActive=200 spring.datasource.carinfo.minIdle=2 spring.datasource.carinfo.initialSize=5 spring.datasource.carinfo.maxWait=60000 spring.datasource.carinfo.timeBetweenEvictionRunsMillis=60000 spring.datasource.carinfo.minEvictableIdleTimeMillis=300000 spring.datasource.carinfo.validationQuery=SELECT 1 FROM DUAL spring.datasource.carinfo.testWhileIdle=true spring.datasource.carinfo.testOnBorrow=false spring.datasource.carinfo.testOnReturn=false spring.datasource.carinfo.poolPreparedStatements=true spring.datasource.carinfo.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.carinfo.filters=stat spring.datasource.carinfo.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #emscartype spring.datasource.emscartype.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.emscartype.driverClassName=com.mysql.jdbc.Driver spring.datasource.emscartype.url=xx spring.datasource.emscartype.username=xx spring.datasource.emscartype.password=xx spring.datasource.emscartype.maxActive=200 spring.datasource.emscartype.minIdle=2 spring.datasource.emscartype.initialSize=5 spring.datasource.emscartype.maxWait=60000 spring.datasource.emscartype.timeBetweenEvictionRunsMillis=60000 spring.datasource.emscartype.minEvictableIdleTimeMillis=300000 spring.datasource.emscartype.validationQuery=SELECT 1 FROM DUAL spring.datasource.emscartype.testWhileIdle=true spring.datasource.emscartype.testOnBorrow=false spring.datasource.emscartype.testOnReturn=false spring.datasource.emscartype.poolPreparedStatements=true spring.datasource.emscartype.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.emscartype.filters=stat spring.datasource.emscartype.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
DataSourceCarInfoProperties.java
package com.chinaway.ems.config.db; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @Description: * @ClassName: DataSourceCarInfoProperties * @Author: Liu FangWei * @Date: 2018/11/8 9:55 * @Version: 1.0 */ @Component //自动注入 @ConfigurationProperties(prefix = "spring.datasource.carinfo") @Data // lombok注解,生成getter/setter等方法 public class DataSourceCarInfoProperties { private String type; private String driverClassName; private String url; private String username; private String password; private int minIdle; private int maxActive; private int maxWait; private String filters; private String connectionProperties; }
DataSourceEmsCarTypeProperties.java
package com.chinaway.ems.config.db; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @Description: * @ClassName: DataSourceCarInfoProperties * @Author: Liu FangWei * @Date: 2018/11/8 9:55 * @Version: 1.0 */ @Component //自动注入 @ConfigurationProperties(prefix = "spring.datasource.emscartype") @Data // lombok注解,生成getter/setter等方法 public class DataSourceEmsCarTypeProperties { private String type; private String driverClassName; private String url; private String username; private String password; private int minIdle; private int maxActive; private int maxWait; private String filters; private String connectionProperties; }
DataSourceCarInfoConfig.java
package com.chinaway.ems.config.db; import javax.sql.DataSource; import com.alibaba.druid.pool.xa.DruidXADataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * @Description: * @ClassName: DataSourceCarInfoConfig * @Author: Liu FangWei * @Date: 2018/10/8 14:24 * @Version: 1.0 */ @Configuration @MapperScan(basePackages = {"com.chinaway.ems.dao.carinfo"}, sqlSessionTemplateRef = "sqlSessionTemplateCarInfo") public class DataSourceCarInfoConfig { @Bean(name = "dataSourceCarInfo") public DataSource dataSourceCarInfo(DataSourceCarInfoProperties dataSourceCarInfoProperties) { DruidXADataSource dataSource = new DruidXADataSource(); BeanUtils.copyProperties(dataSourceCarInfoProperties, dataSource); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(dataSource); xaDataSource.setUniqueResourceName("dataSourceCarInfo"); return xaDataSource; } @Bean(name = "sqlSessionFactoryCarInfo") public SqlSessionFactory sqlSessionFactoryCarInfo(@Qualifier("dataSourceCarInfo") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setTypeAliasesPackage("com.chinaway.ems.domain.carinfo"); //bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/car/*Mapper.xml")); return bean.getObject(); } @Bean(name = "sqlSessionTemplateCarInfo") public SqlSessionTemplate sqlSessionTemplateCarInfo( @Qualifier("sqlSessionFactoryCarInfo") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
DataSourceEmsCarTypeConfig.java
package com.chinaway.ems.config.db; import com.alibaba.druid.pool.xa.DruidXADataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; /** * @Description: * @ClassName: DataSourceEmsCarTypeConfig * @Author: Liu FangWei * @Date: 2018/10/8 14:24 * @Version: 1.0 */ @Configuration @MapperScan(basePackages = {"com.chinaway.ems.dao.emscartype"}, sqlSessionTemplateRef = "sqlSessionTemplateEmsCarType") public class DataSourceEmsCarTypeConfig { @Primary @Bean(name = "dataSourceEmsCarType") public DataSource dataSourceEmsCarType(DataSourceEmsCarTypeProperties dataSourceEmsCarTypeProperties) { DruidXADataSource dataSource = new DruidXADataSource(); BeanUtils.copyProperties(dataSourceEmsCarTypeProperties, dataSource); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(dataSource); xaDataSource.setUniqueResourceName("dataSourceEmsCarType"); return xaDataSource; } @Bean(name = "sqlSessionFactoryEmsCarType") public SqlSessionFactory sqlSessionFactoryEmsCarType(@Qualifier("dataSourceEmsCarType") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setTypeAliasesPackage("com.chinaway.ems.domain.emscartype"); //bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/car/*Mapper.xml")); return bean.getObject(); } @Bean(name = "sqlSessionTemplateEmsCarType") public SqlSessionTemplate sqlSessionTemplateEmsCarType( @Qualifier("sqlSessionFactoryEmsCarType") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
XATransactionManagerConfig.java
package com.chinaway.ems.config.db; import com.atomikos.icatch.jta.UserTransactionImp; import com.atomikos.icatch.jta.UserTransactionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; /** * @Description: * @ClassName: XATransactionManagerConfig * @Author: Liu FangWei * @Date: 2018/11/8 10:24 * @Version: 1.0 */ @Configuration @EnableTransactionManagement public class XATransactionManagerConfig { @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp(); userTransactionImp.setTransactionTimeout(10000); return userTransactionImp; } @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown(false); return userTransactionManager; } @Bean(name = "transactionManager") @DependsOn({"userTransaction", "atomikosTransactionManager"}) public PlatformTransactionManager transactionManager() throws Throwable { return new JtaTransactionManager(userTransaction(), atomikosTransactionManager()); } }
为了控制atomikos的日志输出目录,增加transactions.properties
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory com.atomikos.icatch.log_base_dir=translogs com.atomikos.icatch.console_file_name=tm.out com.atomikos.icatch.log_base_name=tmlog com.atomikos.icatch.tm_unique_name=com.tlw.bpm.engine.atomikos.spring.jdbc.tm com.atomikos.icatch.console_file_limit=10000 com.atomikos.icatch.console_file_count=10 com.atomikos.icatch.max_timeout=600000 com.atomikos.icatch.default_jta_timeout=120000 com.atomikos.icatch.console_log_level=ERROR com.atomikos.icatch.enable_logging=false
因为atomikos频繁的输出日志,项目中用的log4j2,通过配置log4j2.xml让atomikos只打印warn级别以上的日志
通过以上配置就可以实现jta了,记得在调用的service方法上加上@Transactional注解哦。