springboot实现mybatis分布式事务(atomikos)

背景:项目中使用了两个mysql数据源,有一个功能是同时修改两个库里的表数据,需要进行事务控制。项目框架为springcloud+mybatis。

项目结构如下
springboot实现mybatis分布式事务(atomikos)_第1张图片

增加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注解哦。

你可能感兴趣的:(springboot)