(十二)Spring Boot+Druid+Mybatis实现JTA分布式事务

源码下载:点此下载  整合Mybatis 配置两个SqlSessionFactory 分别扫描不同的包
对于分布式事务而言, JTA 是一个不错的解决方案,通常 JTA 需要应用服务器的支持,但在查阅 SpringBoot 的文档时发现,它推荐了 Atomikos 和 Bitronix 两种无需服务器支持的分布式事务组件,在这两个组件中, Atomikos 更受大家的好评,所以我选择使用它.

引入依赖

<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

application.properties

#配置jdbc数据源
#配置多数据源
#数据源名称(与下列配置的数据源对应,多个数据源要配多个名称)
jdbc.datasources=ds,ds1
jdbc.ds.driverClassName=com.mysql.jdbc.Driver
jdbc.ds.url=jdbc\:mysql\://localhost\:3306/ryx?characterEncoding\=UTF-8&pinGlobalTxToPhysicalConnection\=true
jdbc.ds.username=root
jdbc.ds.password=123456


jdbc.ds1.driverClassName=com.mysql.jdbc.Driver
jdbc.ds1.url=jdbc\:mysql\://172.20.1.5\:3306/ryx?characterEncoding\=UTF-8&pinGlobalTxToPhysicalConnection\=true
jdbc.ds1.username=root
jdbc.ds1.password=root
#druid监控平台账号密码
druid.username=root
druid.password=Ruyixing2017
注意:使用Druid配置JTA事务需要使用DruidXADataSource,不要再使用DruidDataSource了

TestMyBatisConfig.java

package com.test.springboot.config.datasource;


import java.sql.SQLException;


import javax.sql.DataSource;


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.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;


import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;


@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.test.springboot.dao", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class TestMyBatisConfig {
	
	@Value("${jdbc.ds.driverClassName}")
	private String driver;
	
	@Value("${jdbc.ds.url}")
	private String url;
	
	@Value("${jdbc.ds.username}")
	private String username;
	
	@Value("${jdbc.ds.password}")
	private String password;


	// 配置数据源
	@Primary
	@Bean(name = "testDataSource",initMethod="init",destroyMethod="close")
	public DataSource dataSource() throws SQLException{
		DruidXADataSource dataSource=new DruidXADataSource();
		//DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(driver);
		dataSource.setUrl(url);
		dataSource.setUsername(username);
        dataSource.setPassword(password);
        //配置最大连接
        dataSource.setMaxActive(300);
        //配置初始连接
        dataSource.setInitialSize(20);
        //配置最小连接
        dataSource.setMinIdle(10);
        //连接等待超时时间
        dataSource.setMaxWait(60000);
        //间隔多久进行检测,关闭空闲连接
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        //一个连接最小生存时间
        dataSource.setMinEvictableIdleTimeMillis(300000);
        //连接等待超时时间 单位为毫秒 缺省启用公平锁,
        //并发效率会有所下降, 如果需要可以通过配置useUnfairLock属性为true使用非公平锁
        dataSource.setUseUnfairLock(true);
        //用来检测是否有效的sql
        dataSource.setValidationQuery("select 'x'");
        dataSource.setTestWhileIdle(true);
        //申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能
        dataSource.setTestOnBorrow(false);
        //归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能
        dataSource.setTestOnReturn(false);
        //打开PSCache,并指定每个连接的PSCache大小启用poolPreparedStatements后,
        //PreparedStatements 和CallableStatements 都会被缓存起来复用,
        //即相同逻辑的SQL可以复用一个游标,这样可以减少创建游标的数量。
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxOpenPreparedStatements(20);
        //配置sql监控的filter
        dataSource.setFilters("stat,wall,log4j");
        try {
            dataSource.init();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(dataSource);
		xaDataSource.setUniqueResourceName("testDataSource");
		/*xaDataSource.setMinPoolSize(10);
		xaDataSource.setMaxPoolSize(300);
		xaDataSource.setMaxLifetime(20000);
		xaDataSource.setBorrowConnectionTimeout(30);
		xaDataSource.setLoginTimeout(30);
		xaDataSource.setMaintenanceInterval(60);
		xaDataSource.setMaxIdleTime(60);
		xaDataSource.setTestQuery("select 'x'");*/
        return xaDataSource;
	}


	@Bean(name = "testSqlSessionFactory")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		//bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/test/springboot/dao/*.xml"));
		return bean.getObject();
	}


	@Bean(name = "testSqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

Test2MyBatisConfig.java

package com.test.springboot.config.datasource;


import java.sql.SQLException;


import javax.sql.DataSource;


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.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;


import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;


@Configuration
@MapperScan(basePackages = "com.test.springboot.dao2", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class Test2MyBatisConfig {
	
	@Value("${jdbc.ds1.driverClassName}")
	private String driver;
	
	@Value("${jdbc.ds1.url}")
	private String url;
	
	@Value("${jdbc.ds1.username}")
	private String username;
	
	@Value("${jdbc.ds1.password}")
	private String password;
	
	@Bean(name = "test2DataSource",initMethod="init",destroyMethod="close")
	public DataSource dataSource() throws SQLException{
		DruidXADataSource dataSource=new DruidXADataSource();
		//DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(driver);
		dataSource.setUrl(url);
		dataSource.setUsername(username);
        dataSource.setPassword(password);
        //配置最大连接
        dataSource.setMaxActive(300);
        //配置初始连接
        dataSource.setInitialSize(20);
        //配置最小连接
        dataSource.setMinIdle(10);
        //连接等待超时时间
        dataSource.setMaxWait(60000);
        //间隔多久进行检测,关闭空闲连接
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        //一个连接最小生存时间
        dataSource.setMinEvictableIdleTimeMillis(300000);
        //连接等待超时时间 单位为毫秒 缺省启用公平锁,
        //并发效率会有所下降, 如果需要可以通过配置useUnfairLock属性为true使用非公平锁
        dataSource.setUseUnfairLock(true);
        //用来检测是否有效的sql
        dataSource.setValidationQuery("select 'x'");
        dataSource.setTestWhileIdle(true);
        //申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能
        dataSource.setTestOnBorrow(false);
        //归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能
        dataSource.setTestOnReturn(false);
        //打开PSCache,并指定每个连接的PSCache大小启用poolPreparedStatements后,
        //PreparedStatements 和CallableStatements 都会被缓存起来复用,
        //即相同逻辑的SQL可以复用一个游标,这样可以减少创建游标的数量。
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxOpenPreparedStatements(20);
        //配置sql监控的filter
        dataSource.setFilters("stat,wall,log4j");
        try {
            dataSource.init();
        } catch (SQLException e) {
           e.printStackTrace();
        }
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(dataSource);
		xaDataSource.setUniqueResourceName("test2DataSource");
		/*xaDataSource.setMinPoolSize(10);
		xaDataSource.setMaxPoolSize(300);
		xaDataSource.setMaxLifetime(20000);
		xaDataSource.setBorrowConnectionTimeout(30);
		xaDataSource.setLoginTimeout(30);
		xaDataSource.setMaintenanceInterval(60);
		xaDataSource.setMaxIdleTime(60);
		xaDataSource.setTestQuery("select 'x'");*/
        return xaDataSource;
	}
	
	@Bean(name = "test2SqlSessionFactory")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		//bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/test/springboot/dao2/*.xml"));
		return bean.getObject();
	}


	@Bean(name = "test2SqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

然后要配置JTA事务管理

TransactionManagerConfig.java

package com.test.springboot.config.datasource;


import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
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 com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;


@Configuration
@ComponentScan
@EnableTransactionManagement
public class TransactionManagerConfig {
	
	@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 {
		UserTransaction userTransaction = userTransaction();
		JtaTransactionManager manager = new JtaTransactionManager(userTransaction,atomikosTransactionManager());
		return manager;
	}


}

测试方法

在需要事务的service方法内注入两个数据库的操作对应的mapper,然后在方法上标注@Transactional注解就可以了,跟之前使用方法没什么两样
@Override
@Transactional
public Integer insetStudent(Student student) {
	Integer integer = studentMapper.insetStudent(student);
	Integer integer2 = studentMapper2.insetStudent(student);
	int a=10/0;
	return integer;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值