feat:增加适配atomikos数据源,支持同一个事务下多次切换数据源 (#481)
* feat:增加适配atomikos数据源,支持同一个事务下多次切换数据源 --------- Co-authored-by: jiazhifeng <wb.jiazhifeng01@mesg.corp.netease.com>
This commit is contained in:
parent
5a4207d097
commit
d0300b5bef
@ -49,6 +49,11 @@
|
|||||||
<artifactId>druid-spring-boot-starter</artifactId>
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.chris2018998</groupId>
|
<groupId>com.github.chris2018998</groupId>
|
||||||
<artifactId>beecp</artifactId>
|
<artifactId>beecp</artifactId>
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
|
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.beecp.BeeCpConfig;
|
||||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.dbcp2.Dbcp2Config;
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.dbcp2.Dbcp2Config;
|
||||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
|
||||||
@ -100,6 +101,11 @@ public class DataSourceProperty {
|
|||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
@NestedConfigurationProperty
|
||||||
private Dbcp2Config dbcp2 = new Dbcp2Config();
|
private Dbcp2Config dbcp2 = new Dbcp2Config();
|
||||||
|
/**
|
||||||
|
* atomikos参数配置
|
||||||
|
*/
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private AtomikosConfig atomikos = new AtomikosConfig();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解密公匙(如果未设置默认使用全局的)
|
* 解密公匙(如果未设置默认使用全局的)
|
||||||
|
@ -17,9 +17,12 @@ package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
|
|||||||
|
|
||||||
import cn.beecp.BeeDataSource;
|
import cn.beecp.BeeDataSource;
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
import com.atomikos.jdbc.AtomikosDataSourceBean;
|
||||||
import com.baomidou.dynamic.datasource.creator.*;
|
import com.baomidou.dynamic.datasource.creator.*;
|
||||||
|
import com.baomidou.dynamic.datasource.tx.AtomikosTransactionFactory;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
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.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -40,7 +43,8 @@ public class DynamicDataSourceCreatorAutoConfiguration {
|
|||||||
public static final int HIKARI_ORDER = 3000;
|
public static final int HIKARI_ORDER = 3000;
|
||||||
public static final int BEECP_ORDER = 4000;
|
public static final int BEECP_ORDER = 4000;
|
||||||
public static final int DBCP2_ORDER = 5000;
|
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
|
@Primary
|
||||||
@Bean
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
|
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
|
||||||
|
|
||||||
import com.baomidou.dynamic.datasource.enums.SeataMode;
|
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.beecp.BeeCpConfig;
|
||||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.dbcp2.Dbcp2Config;
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.dbcp2.Dbcp2Config;
|
||||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
|
||||||
@ -104,6 +105,11 @@ public class DynamicDataSourceProperties {
|
|||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
@NestedConfigurationProperty
|
||||||
private Dbcp2Config dbcp2 = new Dbcp2Config();
|
private Dbcp2Config dbcp2 = new Dbcp2Config();
|
||||||
|
/**
|
||||||
|
* atomikos全局参数配置
|
||||||
|
*/
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private AtomikosConfig atomikos = new AtomikosConfig();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* aop with default ds annotation
|
* aop with default ds annotation
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -48,4 +48,8 @@ public interface DdConstants {
|
|||||||
* DBCP2数据源
|
* DBCP2数据源
|
||||||
*/
|
*/
|
||||||
String DBCP2_DATASOURCE = "org.apache.commons.dbcp2.BasicDataSource";
|
String DBCP2_DATASOURCE = "org.apache.commons.dbcp2.BasicDataSource";
|
||||||
|
/**
|
||||||
|
* Atomikos数据源
|
||||||
|
*/
|
||||||
|
String ATOMIKOS_DATASOURCE = "com.atomikos.jdbc.AtomikosDataSourceBean";
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
6
pom.xml
6
pom.xml
@ -50,6 +50,7 @@
|
|||||||
<mybatis.plus.version>3.5.2</mybatis.plus.version>
|
<mybatis.plus.version>3.5.2</mybatis.plus.version>
|
||||||
<hikaricp.version>2.4.13</hikaricp.version>
|
<hikaricp.version>2.4.13</hikaricp.version>
|
||||||
<druid.version>1.2.14</druid.version>
|
<druid.version>1.2.14</druid.version>
|
||||||
|
<atomikos.version>2.6.9</atomikos.version>
|
||||||
<beeCp.version>3.2.9</beeCp.version>
|
<beeCp.version>3.2.9</beeCp.version>
|
||||||
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
|
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
|
||||||
<p6spy.version>3.9.1</p6spy.version>
|
<p6spy.version>3.9.1</p6spy.version>
|
||||||
@ -114,6 +115,11 @@
|
|||||||
<artifactId>druid-spring-boot-starter</artifactId>
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
<version>${druid.version}</version>
|
<version>${druid.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
|
||||||
|
<version>${atomikos.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.chris2018998</groupId>
|
<groupId>com.github.chris2018998</groupId>
|
||||||
<artifactId>beecp</artifactId>
|
<artifactId>beecp</artifactId>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user