須知:該博客是多數據源的事務問題,還未涉及微服務的事務
1、導入核心的依賴包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
2、配置多數據源
##account表數據庫配置
spring.atomikos.datasource.account.max-pool-size=25
spring.atomikos.datasource.account.min-pool-size=3
spring.atomikos.datasource.account.max-lifetime=20000
spring.atomikos.datasource.account.borrow-connection-timeout=10000
spring.atomikos.datasource.account.unique-resource-name=account
spring.atomikos.datasource.account.xa-properties.url=jdbc:mysql://127.0.0.1:3306/xa_account?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.atomikos.datasource.account.xa-properties.username=root
spring.atomikos.datasource.account.xa-properties.password=root
spring.atomikos.datasource.account.xa-properties.driverClassName=com.mysql.jdbc.Driver
# 初始化大小,最小,最大
spring.atomikos.datasource.account.xa-properties.initialSize=10
spring.atomikos.datasource.account.xa-properties.minIdle=20
spring.atomikos.datasource.account.xa-properties.maxActive=100
## 配置獲取連接等待超時的時間
spring.atomikos.datasource.account.xa-properties.maxWait=60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
spring.atomikos.datasource.account.xa-properties.timeBetweenEvictionRunsMillis=60000
# 配置一個連接在池中最小生存的時間,單位是毫秒
spring.atomikos.datasource.account.xa-properties.minEvictableIdleTimeMillis=300000
spring.atomikos.datasource.account.xa-properties.testWhileIdle=true
spring.atomikos.datasource.account.xa-properties.testOnBorrow=false
spring.atomikos.datasource.account.xa-properties.testOnReturn=false
# 打開PSCache,並且指定每個連接上PSCache的大小
spring.atomikos.datasource.account.xa-properties.poolPreparedStatements=true
spring.atomikos.datasource.account.xa-properties.maxPoolPreparedStatementPerConnectionSize=20
# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
spring.atomikos.datasource.account.xa-properties.filters=stat,slf4j,wall
spring.atomikos.datasource.account.xa-data-source-class-name=com.alibaba.druid.pool.xa.DruidXADataSource
#------------------------------ 分隔符-------------------------------------
##redpacket表數據庫配置
spring.atomikos.datasource.redpacket.max-pool-size=25
spring.atomikos.datasource.redpacket.min-pool-size=3
spring.atomikos.datasource.redpacket.max-lifetime=20000
spring.atomikos.datasource.redpacket.borrow-connection-timeout=10000
spring.atomikos.datasource.redpacket.unique-resource-name=redpacket
spring.atomikos.datasource.redpacket.xa-properties.url=jdbc:mysql://127.0.0.1:3306/xa_red_account?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.atomikos.datasource.redpacket.xa-properties.username=root
spring.atomikos.datasource.redpacket.xa-properties.password=root
spring.atomikos.datasource.redpacket.xa-properties.driverClassName=com.mysql.jdbc.Driver
spring.atomikos.datasource.redpacket.xa-properties.initialSize=10
spring.atomikos.datasource.redpacket.xa-properties.minIdle=20
spring.atomikos.datasource.redpacket.xa-properties.maxActive=100
spring.atomikos.datasource.redpacket.xa-properties.maxWait=60000
spring.atomikos.datasource.redpacket.xa-properties.timeBetweenEvictionRunsMillis=60000
spring.atomikos.datasource.redpacket.xa-properties.minEvictableIdleTimeMillis=300000
spring.atomikos.datasource.redpacket.xa-properties.testWhileIdle=true
spring.atomikos.datasource.redpacket.xa-properties.testOnBorrow=false
spring.atomikos.datasource.redpacket.xa-properties.testOnReturn=false
spring.atomikos.datasource.redpacket.xa-properties.poolPreparedStatements=true
spring.atomikos.datasource.redpacket.xa-properties.maxPoolPreparedStatementPerConnectionSize=20
# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
spring.atomikos.datasource.redpacket.xa-properties.filters=stat,slf4j,wall
spring.atomikos.datasource.redpacket.xa-data-source-class-name=com.alibaba.druid.pool.xa.DruidXADataSource
logging.level.root=INFO
3、第三步:將配置的數據庫連接信息,注入數據源,並且設置druid的監控中心。
MybatisConfiguration :配置DataSource
@Configuration
@EnableConfigurationProperties
@EnableTransactionManagement(proxyTargetClass = true)
public class MybatisConfiguration {
/**
* account數據庫配置前綴.
*/
final static String ACCOUNT_PREFIX = "spring.atomikos.datasource.account";
/**
* redpacket數據庫配置前綴.
*/
final static String REDPACKET_PREFIX = "spring.atomikos.datasource.redpacket";
/**
* The constant logger.
*/
final static Logger logger = LoggerFactory.getLogger(MybatisConfiguration.class);
/**
* 配置druid顯示監控統計信息
* 開啓Druid的監控平臺 http://localhost:8080/druid
*
* @return servlet registration bean
*/
@Bean
public ServletRegistrationBean druidServlet() {
logger.info("Init Druid Servlet Configuration ");
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// IP白名單,不設默認都可以
// servletRegistrationBean.addInitParameter("allow", "192.168.2.25,127.0.0.1");
// IP黑名單(共同存在時,deny優先於allow)
servletRegistrationBean.addInitParameter("deny", "192.168.1.100");
//控制檯管理用戶
servletRegistrationBean.addInitParameter("loginUsername", "root");
servletRegistrationBean.addInitParameter("loginPassword", "root");
//是否能夠重置數據 禁用HTML頁面上的“Reset All”功能
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 註冊一個filterRegistrationBean
*
* @return filter registration bean
*/
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
//添加過濾規則
filterRegistrationBean.addUrlPatterns("/*");
//添加不需要忽略的格式信息
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
/**
* 配置Account數據庫的數據源
*
* @return the data source
*/
@Bean(name = "AccountDataSource")
@ConfigurationProperties(prefix = ACCOUNT_PREFIX) // application.properties中對應屬性的前綴
public DataSource accountDataSource() {
return new AtomikosDataSourceBean();
}
/**
* 配置RedPacket數據庫的數據源
*
* @return the data source
*/
@Bean(name = "RedPacketDataSource")
@ConfigurationProperties(prefix = REDPACKET_PREFIX) // application.properties中對應屬性的前綴
public DataSource redPacketDataSource() {
return new AtomikosDataSourceBean();
}
}
AccountDataSourceConfiguration 作用:配置account的數據源的sessionfactory ,同時關聯mybaits
@Configuration
@MapperScan(basePackages = {"com.agan.dtp.atomikos.mapper.account.mapper"}, sqlSessionFactoryRef = "accountSqlSessionFactory")
public class AccountDataSourceConfiguration {
/**
* The constant MAPPER_XML_LOCATION.
*/
public static final String MAPPER_XML_LOCATION = "classpath*:com/transaction/dtp/atomikos/mapper/account/mapper/*.xml";
/**
* The Open plat form data source.
*/
@Autowired
@Qualifier("AccountDataSource")
DataSource accountDataSource;
/**
* 配置Sql Session模板
*
* @return the sql session template
* @throws Exception the exception
*/
@Bean
public SqlSessionTemplate springSqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(accountSqlSessionFactory());
}
/**
* 配置SQL Session工廠
*
* @return the sql session factory
* @throws Exception the exception
*/
@Bean
public SqlSessionFactory accountSqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(accountDataSource);
//指定XML文件路徑
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_XML_LOCATION));
return factoryBean.getObject();
}
}
RedAccountDataSourceConfiguration 作用:配置RedAccount的數據源的sessionfactory ,同時關聯mybaits
@Configuration
@MapperScan(basePackages = {"com.agan.dtp.atomikos.mapper.redaccount.mapper"}, sqlSessionFactoryRef = "redPacketSqlSessionFactory")
public class RedAccountDataSourceConfiguration {
/**
* The constant MAPPER_XML_LOCATION.
*/
public static final String MAPPER_XML_LOCATION = "classpath*:com/transaction/dtp/atomikos/mapper/redaccount/mapper/*.xml";
/**
* The Open plat form data source.
*/
@Autowired
@Qualifier("RedPacketDataSource")
DataSource redPacketDataSource;
/**
* Open plat form sql session template sql session template.
*
* @return the sql session template
* @throws Exception the exception
*/
@Bean
public SqlSessionTemplate redPacketSqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(redPacketSqlSessionFactory());
}
/**
* Open plat form sql session factory sql session factory.
*
* @return the sql session factory
* @throws Exception the exception
*/
@Bean
public SqlSessionFactory redPacketSqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(redPacketDataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_XML_LOCATION));
return factoryBean.getObject();
}
}
第四步:service體驗 atomikos
@Service
public class PayServiceImpl implements PayService {
@Autowired
private CapitalAccountMapper capitalAccountMapper;
@Autowired
private RedPacketAccountMapper redPacketAccountMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void pay(int userId,int account, int redAccount) {
CapitalAccount ca=new CapitalAccount();
ca.setUserId(userId);
CapitalAccount capitalDTO=this.capitalAccountMapper.selectOne(ca);
System.out.println(capitalDTO);
//賬戶餘額扣除
capitalDTO.setBalanceAmount(capitalDTO.getBalanceAmount()-account);
this.capitalAccountMapper.updateByPrimaryKey(capitalDTO);
RedPacketAccount red= new RedPacketAccount();
red.setUserId(userId);
RedPacketAccount redDTO=this.redPacketAccountMapper.selectOne(red);
System.out.println(redDTO);
//紅包餘額扣除
redDTO.setBalanceAmount(redDTO.getBalanceAmount()-redAccount);
this.redPacketAccountMapper.updateByPrimaryKey(redDTO);
//int n=9/0;//測試
}
}
第五步: junit 測試
@RunWith(SpringRunner.class)
@SpringBootTest
public class AtomikosTests {
@Autowired
PayService payService;
@Test
//@Transactional
public void contextLoads() {
try {
this.payService.pay(1,10,10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
第六步: 查看Atomikos的日誌
1.如何測試atomikos的事務運行結果?
查看Atomikos的日誌,默認情況下,在項目的根目錄下會自動創建transaction-logs文件夾,每個Atomikos實例都會有一個全局ID,這個ID爲Atomikos運行機器的IP地址;
這個唯一ID會自動關聯多個數據庫的事務信息,也就是會關聯分支事務id.
2.atomikos日誌的自定義配置
spring.jta.log-dir=transaction-logs