背景
“项目中使用了两个 mysql 数据源,有一个功能是同时修改两个库里的表数据,需要进行事务控制。项目框架为 springcloud+mybatis。
”
项目结构
![eece0e3f68dc51a3e27ba599c5b7f5f6.png](https://i-blog.csdnimg.cn/blog_migrate/ac677d53c762dc0e18c78dd252811453.png)
增加 maven 依赖
<!--分布式事务支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<!--lombok jar 自动get set-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
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 级别以上的日志
<Logger >
<AppenderRef ref="AsyncAppender"/>
<AppenderRef ref="Console"/>
</Logger>
通过以上配置就可以实现 jta 了,记得在调用的 service 方法上加上 @Transactional 注解哦。
作者:lfwer
来源链接:
https://blog.csdn.net/lfwer/article/details/83901058