feat:增加适配atomikos数据源,支持同一个事务下多次切换数据源 (#481)

* feat:增加适配atomikos数据源,支持同一个事务下多次切换数据源

---------

Co-authored-by: jiazhifeng <wb.jiazhifeng01@mesg.corp.netease.com>
This commit is contained in:
JiaZhiFeng 2023-03-07 09:53:35 +08:00 committed by GitHub
parent 5a4207d097
commit d0300b5bef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 239 additions and 1 deletions

View File

@ -49,6 +49,11 @@
<artifactId>druid-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.chris2018998</groupId>
<artifactId>beecp</artifactId>

View File

@ -0,0 +1,65 @@
package com.baomidou.dynamic.datasource.creator;
import com.baomidou.dynamic.datasource.enums.XADataSourceEnum;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.atomikos.AtomikosConfig;
import com.baomidou.dynamic.datasource.toolkit.ConfigMergeCreator;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import javax.sql.DataSource;
import java.util.Properties;
import static com.baomidou.dynamic.datasource.support.DdConstants.ATOMIKOS_DATASOURCE;
/**
* Atomikos数据源配置
*
* @author <a href="mailto:312290710@qq.com">jiazhifeng</a>
* @date 2023/03/02 10:20
*/
public class AtomikosDataSourceCreator extends AbstractDataSourceCreator implements DataSourceCreator, InitializingBean {
private static final ConfigMergeCreator<AtomikosConfig, AtomikosConfig> MERGE_CREATOR = new ConfigMergeCreator<>("AtomikosConfig", AtomikosConfig.class, AtomikosConfig.class);
private AtomikosConfig atomikosConfig;
@Override
public DataSource doCreateDataSource(DataSourceProperty dataSourceProperty) {
AtomikosConfig config = MERGE_CREATOR.create(atomikosConfig, dataSourceProperty.getAtomikos());
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
DbType dbType = JdbcUtils.getDbType(dataSourceProperty.getUrl());
xaDataSource.setXaDataSourceClassName(XADataSourceEnum.getByDbType(dbType));
Properties xaProperties = new Properties();
xaProperties.setProperty("url", dataSourceProperty.getUrl());
xaProperties.setProperty("user", dataSourceProperty.getUsername());
xaProperties.setProperty("password", dataSourceProperty.getPassword());
xaDataSource.setXaProperties(xaProperties);
xaDataSource.setUniqueResourceName(dataSourceProperty.getPoolName());
xaDataSource.setMinPoolSize(config.getMinPoolSize());
xaDataSource.setMaxPoolSize(config.getMaxPoolSize());
xaDataSource.setBorrowConnectionTimeout(config.getBorrowConnectionTimeout());
xaDataSource.setReapTimeout(config.getReapTimeout());
xaDataSource.setMaxIdleTime(config.getMaxIdleTime());
xaDataSource.setTestQuery(config.getTestQuery());
xaDataSource.setMaintenanceInterval(config.getMaintenanceInterval());
xaDataSource.setDefaultIsolationLevel(config.getDefaultIsolationLevel());
xaDataSource.setMaxLifetime(config.getMaxLifetime());
return xaDataSource;
}
@Override
public boolean support(DataSourceProperty dataSourceProperty) {
Class<? extends DataSource> type = dataSourceProperty.getType();
DbType dbType = JdbcUtils.getDbType(dataSourceProperty.getUrl());
return (type == null || ATOMIKOS_DATASOURCE.equals(type.getName())) && XADataSourceEnum.contains(dbType);
}
@Override
public void afterPropertiesSet() throws Exception {
atomikosConfig = properties.getAtomikos();
}
}

View File

@ -0,0 +1,45 @@
package com.baomidou.dynamic.datasource.enums;
import com.baomidou.mybatisplus.annotation.DbType;
import lombok.Getter;
/**
* 目前支持的XA数据源
*
* @author <a href="mailto:312290710@qq.com">jiazhifeng</a>
* @date 2023/03/02 23:05
*/
@Getter
public enum XADataSourceEnum {
ORACLE(DbType.ORACLE,"oracle.jdbc.xa.client.OracleXADataSource"),
MYSQL(DbType.MYSQL, "com.mysql.cj.jdbc.MysqlXADataSource"),
POSTGRE_SQL(DbType.POSTGRE_SQL, "org.postgresql.xa.PGXADataSource"),
H2(DbType.H2, "org.h2.jdbcx.JdbcDataSource"),
;
private final DbType dbType;
private final String xaDataSourceClassName;
XADataSourceEnum(DbType dbType, String xaDataSourceClassName) {
this.dbType = dbType;
this.xaDataSourceClassName = xaDataSourceClassName;
}
public static boolean contains(DbType dbType){
for (XADataSourceEnum item : values()) {
if (item.getDbType() == dbType) {
return true;
}
}
return false;
}
public static String getByDbType(DbType dbType){
for (XADataSourceEnum item : values()) {
if (item.getDbType() == dbType) {
return item.getXaDataSourceClassName();
}
}
return null;
}
}

View File

@ -15,6 +15,7 @@
*/
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.atomikos.AtomikosConfig;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.beecp.BeeCpConfig;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.dbcp2.Dbcp2Config;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
@ -100,6 +101,11 @@ public class DataSourceProperty {
*/
@NestedConfigurationProperty
private Dbcp2Config dbcp2 = new Dbcp2Config();
/**
* atomikos参数配置
*/
@NestedConfigurationProperty
private AtomikosConfig atomikos = new AtomikosConfig();
/**
* 解密公匙(如果未设置默认使用全局的)

View File

@ -17,9 +17,12 @@ package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
import cn.beecp.BeeDataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.baomidou.dynamic.datasource.creator.*;
import com.baomidou.dynamic.datasource.tx.AtomikosTransactionFactory;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.ibatis.transaction.TransactionFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@ -40,7 +43,8 @@ public class DynamicDataSourceCreatorAutoConfiguration {
public static final int HIKARI_ORDER = 3000;
public static final int BEECP_ORDER = 4000;
public static final int DBCP2_ORDER = 5000;
public static final int DEFAULT_ORDER = 6000;
public static final int ATOMIKOS_ORDER = 6000;
public static final int DEFAULT_ORDER = 7000;
@Primary
@Bean
@ -118,4 +122,23 @@ public class DynamicDataSourceCreatorAutoConfiguration {
}
}
/**
* 存在Atomikos数据源时, 加入创建器
*/
@ConditionalOnClass({AtomikosDataSourceBean.class})
@Configuration
static class AtomikosDataSourceCreatorConfiguration {
@Bean
@Order(ATOMIKOS_ORDER)
public AtomikosDataSourceCreator atomikosDataSourceCreator() {
return new AtomikosDataSourceCreator();
}
@Bean
public TransactionFactory atomikosTransactionFactory() {
return new AtomikosTransactionFactory();
}
}
}

View File

@ -16,6 +16,7 @@
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
import com.baomidou.dynamic.datasource.enums.SeataMode;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.atomikos.AtomikosConfig;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.beecp.BeeCpConfig;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.dbcp2.Dbcp2Config;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
@ -104,6 +105,11 @@ public class DynamicDataSourceProperties {
*/
@NestedConfigurationProperty
private Dbcp2Config dbcp2 = new Dbcp2Config();
/**
* atomikos全局参数配置
*/
@NestedConfigurationProperty
private AtomikosConfig atomikos = new AtomikosConfig();
/**
* aop with default ds annotation

View File

@ -0,0 +1,49 @@
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure.atomikos;
import lombok.Data;
/**
* Atomikos 配置
*
* @author <a href="mailto:312290710@qq.com">jiazhifeng</a>
* @date 2023/03/02 10:20
*/
@Data
public class AtomikosConfig {
/**
* 设置最小池大小连接的数量不会低于该值池将在初始化期间打开此数量的连接可选默认为 1
*/
private int minPoolSize = 1;
/**
* 设置最大池大小池连接的数量不会超过这个值可选默认为 10
*/
private int maxPoolSize = 10;
/**
* 设置池在池为空时等待连接在池中可用的最长时间以秒为单位默认为 30
*/
private int borrowConnectionTimeout = 30;
/**
* 设置连接池在声明连接之前允许使用连接的时间量以秒为单位默认值为 0无超时
*/
private int reapTimeout = 0;
/**
* 设置未使用的多余连接应保留在池中的最大秒数选修的注意超额连接是在 minPoolSize 限制之上创建的连接默认值为 60
*/
private int maxIdleTime = 60;
/**
* 设置用于在返回连接之前验证连接的 SQL 查询或语句
*/
private String testQuery = "SELECT 1";
/**
* 设置池维护线程的维护间隔以秒为单位的时间间隔如果未设置或不是正数则将使用池的默认值60
*/
private int maintenanceInterval = 60;
/**
* 设置此数据源返回的连接的默认隔离级别负值将被忽略并导致特定于供应商的 JDBC 驱动程序或 DBMS 内部默认值
*/
private int defaultIsolationLevel = -1;
/**
* 设置连接在自动销毁之前保留在池中的最大秒数可选默认为 0无限制
*/
private int maxLifetime = 60;
}

View File

@ -48,4 +48,8 @@ public interface DdConstants {
* DBCP2数据源
*/
String DBCP2_DATASOURCE = "org.apache.commons.dbcp2.BasicDataSource";
/**
* Atomikos数据源
*/
String ATOMIKOS_DATASOURCE = "com.atomikos.jdbc.AtomikosDataSourceBean";
}

View File

@ -0,0 +1,29 @@
package com.baomidou.dynamic.datasource.tx;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.mybatis.spring.transaction.SpringManagedTransaction;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import javax.sql.DataSource;
/**
* Atomikos事务适配-多数据源切换
*
* @author <a href="mailto:312290710@qq.com">jiazhifeng</a>
* @date 2023/03/02 10:20
*/
public class AtomikosTransactionFactory extends SpringManagedTransactionFactory {
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
DataSource determineDataSource = dataSource;
// e.g:ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (dataSource instanceof DynamicRoutingDataSource) {
determineDataSource = ((DynamicRoutingDataSource)dataSource).determineDataSource();
}
return new SpringManagedTransaction(determineDataSource);
}
}

View File

@ -50,6 +50,7 @@
<mybatis.plus.version>3.5.2</mybatis.plus.version>
<hikaricp.version>2.4.13</hikaricp.version>
<druid.version>1.2.14</druid.version>
<atomikos.version>2.6.9</atomikos.version>
<beeCp.version>3.2.9</beeCp.version>
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
<p6spy.version>3.9.1</p6spy.version>
@ -114,6 +115,11 @@
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
<version>${atomikos.version}</version>
</dependency>
<dependency>
<groupId>com.github.chris2018998</groupId>
<artifactId>beecp</artifactId>