featL 完善druid3兼容,完善javadoc

This commit is contained in:
TaoYu 2023-07-18 16:15:23 +08:00
parent f5541d2b4e
commit 68a090fc99
60 changed files with 888 additions and 380 deletions

View File

@ -76,20 +76,24 @@ dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成
1. 引入dynamic-datasource-spring-boot-starter。
spring-boot 1.5.x 2.x.x
```xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
```
spring-boot3及以上
```xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${version}</version>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${version}</version>
</dependency>
```
@ -117,21 +121,39 @@ spring:
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver
#......省略
#以上会配置一个默认库master一个组slave下有两个子库slave_1,slave_2
#......省略
#以上会配置一个默认库master一个组slave下有两个子库slave_1,slave_2
```
```yaml
# 多主多从 纯粹多库记得设置primary 混合配置
spring: spring: spring:
datasource: datasource: datasource:
dynamic: dynamic: dynamic:
datasource: datasource: datasource:
master_1: mysql: master:
master_2: oracle: slave_1:
slave_1: sqlserver: slave_2:
slave_2: postgresql: oracle_1:
slave_3: h2: oracle_2:
spring:
spring:
spring:
datasource:
datasource:
datasource:
dynamic:
dynamic:
dynamic:
datasource:
datasource:
datasource:
master_1:
mysql:
master:
master_2:
oracle:
slave_1:
slave_1:
sqlserver:
slave_2:
slave_2:
postgresql:
oracle_1:
slave_3:
h2:
oracle_2:
```
3. 使用 **@DS** 切换数据源。
@ -144,21 +166,22 @@ spring: spring: spri
| @DS("dsName") | dsName可以为组名也可以为具体某个库的名称 |
```java
@Service
@DS("slave")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("slave_1")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
@Override
@DS("slave_1")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
```

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</parent>
<artifactId>dynamic-datasource-creator</artifactId>

View File

@ -15,15 +15,16 @@
*/
package com.baomidou.dynamic.datasource.creator.atomikos;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* Atomikos 配置
*
* @author <a href="mailto:312290710@qq.com">jiazhifeng</a>
* @date 2023/03/02 10:20
*/
@Data
@Getter
@Setter
public class AtomikosConfig {
/**
* 设置最小池大小连接的数量不会低于该值池将在初始化期间打开此数量的连接可选默认为 1

View File

@ -32,7 +32,6 @@ import java.util.Properties;
* Atomikos数据源配置
*
* @author <a href="mailto:312290710@qq.com">jiazhifeng</a>
* @date 2023/03/02 10:20
*/
@NoArgsConstructor
@AllArgsConstructor

View File

@ -15,7 +15,8 @@
*/
package com.baomidou.dynamic.datasource.creator.beecp;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.Properties;
@ -25,7 +26,8 @@ import java.util.Properties;
* @author TaoYu
* @since 3.3.4
*/
@Data
@Getter
@Setter
public class BeeCpConfig {
private String defaultCatalog;

View File

@ -15,7 +15,8 @@
*/
package com.baomidou.dynamic.datasource.creator.dbcp;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Set;
@ -25,7 +26,8 @@ import java.util.Set;
*
* @author TaoYu
*/
@Data
@Getter
@Setter
public class Dbcp2Config {
private Boolean defaultAutoCommit;

View File

@ -15,8 +15,8 @@
*/
package com.baomidou.dynamic.datasource.creator.druid;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import lombok.Getter;
import lombok.Setter;
import java.util.*;
@ -26,8 +26,8 @@ import java.util.*;
* @author TaoYu
* @since 1.2.0
*/
@Data
@Slf4j
@Getter
@Setter
public class DruidConfig {
private Integer initialSize;

View File

@ -15,11 +15,12 @@
*/
package com.baomidou.dynamic.datasource.creator.druid;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.toolkit.DsConfigUtil;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
@ -35,7 +36,15 @@ import java.util.Set;
@Slf4j
public final class DruidConfigUtil {
private static final Map<String, Method> METHODS = DsConfigUtil.getGetterMethods(DruidConfig.class);
private static final String FILTERS = "druid.filters";
private static final String CONFIG_STR = "config";
private static final String STAT_STR = "stat";
private static final Map<String, PropertyDescriptor> CONFIG_DESCRIPTOR_MAP = DsConfigUtil.getPropertyDescriptorMap(DruidConfig.class);
private static final Map<String, PropertyDescriptor> DATASOURCE_DESCRIPTOR_MAP = DsConfigUtil.getPropertyDescriptorMap(DruidDataSource.class);
private static final Class<?> CLAZZ = DruidDataSource.class;
/**
* 根据全局配置和本地配置结合转换为Properties
@ -44,40 +53,44 @@ public final class DruidConfigUtil {
* @param c 当前配置
* @return Druid配置
*/
@SneakyThrows
public static Properties mergeConfig(DruidConfig g, @NonNull DruidConfig c) {
Properties properties = new Properties();
for (Map.Entry<String, Method> entry : METHODS.entrySet()) {
for (Map.Entry<String, PropertyDescriptor> entry : CONFIG_DESCRIPTOR_MAP.entrySet()) {
String key = entry.getKey();
Method readMethod = entry.getValue();
PropertyDescriptor descriptor = entry.getValue();
Method readMethod = descriptor.getReadMethod();
Class<?> returnType = readMethod.getReturnType();
if (List.class.isAssignableFrom(returnType) || Set.class.isAssignableFrom(returnType) || Map.class.isAssignableFrom(returnType) || Properties.class.isAssignableFrom(returnType)) {
continue;
}
Object cValue = readMethod.invoke(c);
if (cValue != null) {
properties.setProperty("druid." + key, String.valueOf(cValue));
continue;
}
if (g != null) {
Object gValue = readMethod.invoke(g);
if (gValue != null) {
properties.setProperty("druid." + key, String.valueOf(gValue));
try {
Object cValue = readMethod.invoke(c);
if (cValue != null) {
properties.setProperty("druid." + key, String.valueOf(cValue));
continue;
}
if (g != null) {
Object gValue = readMethod.invoke(g);
if (gValue != null) {
properties.setProperty("druid." + key, String.valueOf(gValue));
}
}
} catch (Exception e) {
log.warn("druid current could not set [" + key + " ]", e);
}
}
//filters单独处理默认了stat
String filters = getValue(g, c, "filter");
if (filters == null) {
filters = DruidConsts.STAT_STR;
filters = STAT_STR;
}
String publicKey = getValue(g, c, "publicKey");
boolean configFilterExist = publicKey != null && publicKey.length() > 0;
if (publicKey != null && publicKey.length() > 0 && !filters.contains(DruidConsts.CONFIG_STR)) {
filters += "," + DruidConsts.CONFIG_STR;
if (publicKey != null && publicKey.length() > 0 && !filters.contains(CONFIG_STR)) {
filters += "," + CONFIG_STR;
}
properties.setProperty(DruidConsts.FILTERS, filters);
properties.setProperty(FILTERS, filters);
Properties connectProperties = new Properties();
Properties cConnectionProperties = c.getConnectionProperties();
@ -98,8 +111,18 @@ public final class DruidConfigUtil {
return properties;
}
/**
* @param g 全局配置
* @param c 当前配置
* @param field 字段
* @return 字段值
*/
public static String getValue(DruidConfig g, @NonNull DruidConfig c, String field) {
Method method = METHODS.get(field);
PropertyDescriptor propertyDescriptor = CONFIG_DESCRIPTOR_MAP.get(field);
if (propertyDescriptor == null) {
return null;
}
Method method = propertyDescriptor.getReadMethod();
if (method == null) {
return null;
}
@ -120,4 +143,41 @@ public final class DruidConfigUtil {
return null;
}
/**
* 设置DruidDataSource的值
*
* @param dataSource DruidDataSource
* @param field 字段
* @param g 全局配置
* @param c 当前配置
*/
public static void setValue(DruidDataSource dataSource, String field, DruidConfig g, DruidConfig c) {
PropertyDescriptor descriptor = DATASOURCE_DESCRIPTOR_MAP.get(field);
if (descriptor == null) {
log.warn("druid current not support [" + field + " ]");
return;
}
Method writeMethod = descriptor.getWriteMethod();
if (writeMethod == null) {
log.warn("druid current could not set [" + field + " ]");
return;
}
try {
Method configReadMethod = CONFIG_DESCRIPTOR_MAP.get(field).getReadMethod();
Object value = configReadMethod.invoke(c);
if (value != null) {
writeMethod.invoke(dataSource, value);
return;
}
if (g != null) {
value = configReadMethod.invoke(g);
if (value != null) {
writeMethod.invoke(dataSource, value);
}
}
} catch (Exception e) {
log.warn("druid current set [" + field + " ] error");
}
}
}

View File

@ -1,63 +0,0 @@
/*
* Copyright © 2018 organization baomidou
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.dynamic.datasource.creator.druid;
/**
* Druid 配置属性
*
* @author TaoYu
* @since 2020/1/27
*/
public interface DruidConsts {
String INITIAL_SIZE = "druid.initialSize";
String MAX_ACTIVE = "druid.maxActive";
String MIN_IDLE = "druid.minIdle";
String MAX_WAIT = "druid.maxWait";
String TIME_BETWEEN_EVICTION_RUNS_MILLIS = "druid.timeBetweenEvictionRunsMillis";
String TIME_BETWEEN_LOG_STATS_MILLIS = "druid.timeBetweenLogStatsMillis";
String MIN_EVICTABLE_IDLE_TIME_MILLIS = "druid.minEvictableIdleTimeMillis";
String MAX_EVICTABLE_IDLE_TIME_MILLIS = "druid.maxEvictableIdleTimeMillis";
String KEEPALIVE_BETWEEN_TIME_MILLIS = "druid.keepAliveBetweenTimeMillis";
String TEST_WHILE_IDLE = "druid.testWhileIdle";
String TEST_ON_BORROW = "druid.testOnBorrow";
String VALIDATION_QUERY = "druid.validationQuery";
String USE_GLOBAL_DATA_SOURCE_STAT = "druid.useGlobalDataSourceStat";
String ASYNC_INIT = "druid.asyncInit";
String FILTERS = "druid.filters";
String CLEAR_FILTERS_ENABLE = "druid.clearFiltersEnable";
String RESET_STAT_ENABLE = "druid.resetStatEnable";
String NOT_FULL_TIMEOUT_RETRY_COUNT = "druid.notFullTimeoutRetryCount";
String MAX_WAIT_THREAD_COUNT = "druid.maxWaitThreadCount";
String FAIL_FAST = "druid.failFast";
String PHY_TIMEOUT_MILLIS = "druid.phyTimeoutMillis";
String PHY_MAX_USE_COUNT = "druid.phyMaxUseCount";
String KEEP_ALIVE = "druid.keepAlive";
String POOL_PREPARED_STATEMENTS = "druid.poolPreparedStatements";
String INIT_VARIANTS = "druid.initVariants";
String INIT_GLOBAL_VARIANTS = "druid.initGlobalVariants";
String USE_UNFAIR_LOCK = "druid.useUnfairLock";
String KILL_WHEN_SOCKET_READ_TIMEOUT = "druid.killWhenSocketReadTimeout";
String MAX_POOL_PREPARED_STATEMENT_PER_CONNECTION_SIZE = "druid.maxPoolPreparedStatementPerConnectionSize";
String INIT_CONNECTION_SQLS = "druid.initConnectionSqls";
String CONFIG_STR = "config";
String STAT_STR = "stat";
}

View File

@ -35,10 +35,7 @@ import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.*;
/**
* Druid数据源创建器
@ -51,12 +48,33 @@ import java.util.Properties;
@AllArgsConstructor
public class DruidDataSourceCreator implements DataSourceCreator {
private static final Set<String> PARAMS = new HashSet<>();
private static Method configMethod = null;
static {
fetchMethod();
}
static {
PARAMS.add("defaultCatalog");
PARAMS.add("defaultAutoCommit");
PARAMS.add("defaultReadOnly");
PARAMS.add("defaultTransactionIsolation");
PARAMS.add("testOnReturn");
PARAMS.add("validationQueryTimeout");
PARAMS.add("sharePreparedStatements");
PARAMS.add("connectionErrorRetryAttempts");
PARAMS.add("breakAfterAcquireFailure");
PARAMS.add("removeAbandonedTimeoutMillis");
PARAMS.add("removeAbandoned");
PARAMS.add("logAbandoned");
PARAMS.add("queryTimeout");
PARAMS.add("transactionQueryTimeout");
PARAMS.add("timeBetweenConnectErrorMillis");
PARAMS.add("connectTimeout");
PARAMS.add("socketTimeout");
}
// @Autowired(required = false)
// private ApplicationContext applicationContext;
private DruidConfig gConfig;
@ -105,7 +123,9 @@ public class DruidDataSourceCreator implements DataSourceCreator {
//连接参数单独设置
dataSource.setConnectProperties(config.getConnectionProperties());
//设置druid内置properties不支持的的参数
this.setParam(dataSource, config);
for (String param : PARAMS) {
DruidConfigUtil.setValue(dataSource, param, gConfig, config);
}
if (Boolean.FALSE.equals(dataSourceProperty.getLazy())) {
try {
@ -157,106 +177,6 @@ public class DruidDataSourceCreator implements DataSourceCreator {
return proxyFilters;
}
private void setParam(DruidDataSource dataSource, DruidConfig config) {
String defaultCatalog = config.getDefaultCatalog() == null ? gConfig.getDefaultCatalog() : config.getDefaultCatalog();
if (defaultCatalog != null) {
dataSource.setDefaultCatalog(defaultCatalog);
}
Boolean defaultAutoCommit = config.getDefaultAutoCommit() == null ? gConfig.getDefaultAutoCommit() : config.getDefaultAutoCommit();
if (defaultAutoCommit != null && !defaultAutoCommit) {
dataSource.setDefaultAutoCommit(false);
}
Boolean defaultReadOnly = config.getDefaultReadOnly() == null ? gConfig.getDefaultReadOnly() : config.getDefaultReadOnly();
if (defaultReadOnly != null) {
dataSource.setDefaultReadOnly(defaultReadOnly);
}
Integer defaultTransactionIsolation = config.getDefaultTransactionIsolation() == null ? gConfig.getDefaultTransactionIsolation() : config.getDefaultTransactionIsolation();
if (defaultTransactionIsolation != null) {
dataSource.setDefaultTransactionIsolation(defaultTransactionIsolation);
}
Boolean testOnReturn = config.getTestOnReturn() == null ? gConfig.getTestOnReturn() : config.getTestOnReturn();
if (testOnReturn != null && testOnReturn) {
dataSource.setTestOnReturn(true);
}
Integer validationQueryTimeout =
config.getValidationQueryTimeout() == null ? gConfig.getValidationQueryTimeout() : config.getValidationQueryTimeout();
if (validationQueryTimeout != null && !validationQueryTimeout.equals(-1)) {
dataSource.setValidationQueryTimeout(validationQueryTimeout);
}
Boolean sharePreparedStatements =
config.getSharePreparedStatements() == null ? gConfig.getSharePreparedStatements() : config.getSharePreparedStatements();
if (sharePreparedStatements != null && sharePreparedStatements) {
dataSource.setSharePreparedStatements(true);
}
Integer connectionErrorRetryAttempts =
config.getConnectionErrorRetryAttempts() == null ? gConfig.getConnectionErrorRetryAttempts()
: config.getConnectionErrorRetryAttempts();
if (connectionErrorRetryAttempts != null && !connectionErrorRetryAttempts.equals(1)) {
dataSource.setConnectionErrorRetryAttempts(connectionErrorRetryAttempts);
}
Boolean breakAfterAcquireFailure =
config.getBreakAfterAcquireFailure() == null ? gConfig.getBreakAfterAcquireFailure() : config.getBreakAfterAcquireFailure();
if (breakAfterAcquireFailure != null && breakAfterAcquireFailure) {
dataSource.setBreakAfterAcquireFailure(true);
}
Integer timeout = config.getRemoveAbandonedTimeoutMillis() == null ? gConfig.getRemoveAbandonedTimeoutMillis()
: config.getRemoveAbandonedTimeoutMillis();
if (timeout != null) {
dataSource.setRemoveAbandonedTimeoutMillis(timeout);
}
Boolean abandoned = config.getRemoveAbandoned() == null ? gConfig.getRemoveAbandoned() : config.getRemoveAbandoned();
if (abandoned != null) {
dataSource.setRemoveAbandoned(abandoned);
}
Boolean logAbandoned = config.getLogAbandoned() == null ? gConfig.getLogAbandoned() : config.getLogAbandoned();
if (logAbandoned != null) {
dataSource.setLogAbandoned(logAbandoned);
}
Integer queryTimeOut = config.getQueryTimeout() == null ? gConfig.getQueryTimeout() : config.getQueryTimeout();
if (queryTimeOut != null) {
dataSource.setQueryTimeout(queryTimeOut);
}
Integer transactionQueryTimeout =
config.getTransactionQueryTimeout() == null ? gConfig.getTransactionQueryTimeout() : config.getTransactionQueryTimeout();
if (transactionQueryTimeout != null) {
dataSource.setTransactionQueryTimeout(transactionQueryTimeout);
}
Long timeBetweenConnectErrorMillis =
config.getTimeBetweenConnectErrorMillis() == null ? gConfig.getTimeBetweenConnectErrorMillis() : config.getTimeBetweenConnectErrorMillis();
if (timeBetweenConnectErrorMillis != null) {
dataSource.setTimeBetweenConnectErrorMillis(timeBetweenConnectErrorMillis);
}
// since druid 1.2.12
Integer connectTimeout = config.getConnectTimeout() == null ? gConfig.getConnectTimeout() : config.getConnectTimeout();
if (connectTimeout != null) {
try {
DruidDataSource.class.getMethod("setConnectTimeout", int.class);
dataSource.setConnectTimeout(connectTimeout);
} catch (NoSuchMethodException e) {
log.warn("druid current not support connectTimeout,please update druid 1.2.12 +");
}
}
Integer socketTimeout = config.getSocketTimeout() == null ? gConfig.getSocketTimeout() : config.getSocketTimeout();
if (socketTimeout != null) {
try {
DruidDataSource.class.getMethod("setSocketTimeout", int.class);
dataSource.setSocketTimeout(socketTimeout);
} catch (NoSuchMethodException e) {
log.warn("druid current not support setSocketTimeout,please update druid 1.2.12 +");
}
}
}
@Override
public boolean support(DataSourceProperty dataSourceProperty) {
Class<? extends DataSource> type = dataSourceProperty.getType();

View File

@ -36,14 +36,15 @@ public final class DruidLogConfigUtil {
/**
* 根据当前的配置和全局的配置生成druid的日志filter
*
* @param c 当前配置
* @param g 全局配置
* @param clazz 日志类
* @param c 当前配置
* @param g 全局配置
* @return 日志filter
*/
public static LogFilter initFilter(Class<? extends LogFilter> clazz, Map<String, Object> c, Map<String, Object> g) {
try {
LogFilter filter = clazz.newInstance();
Map<String, Object> params = DsConfigUtil.mergeConfig(c, g);
LogFilter filter = clazz.getDeclaredConstructor().newInstance();
Map<String, Object> params = DsConfigUtil.mergeMap(c, g);
for (Map.Entry<String, Object> item : params.entrySet()) {
String key = DsConfigUtil.lineToUpper(item.getKey());
Method method = METHODS.get(key);

View File

@ -49,7 +49,7 @@ public final class DruidStatConfigUtil {
*/
public static StatFilter toStatFilter(Map<String, Object> c, Map<String, Object> g) {
StatFilter filter = new StatFilter();
Map<String, Object> map = DsConfigUtil.mergeConfig(c, g);
Map<String, Object> map = DsConfigUtil.mergeMap(c, g);
for (Map.Entry<String, Object> item : map.entrySet()) {
String key = DsConfigUtil.lineToUpper(item.getKey());
Method method = METHODS.get(key);

View File

@ -42,7 +42,7 @@ public final class DruidWallConfigUtil {
*/
public static WallConfig toWallConfig(Map<String, Object> c, Map<String, Object> g) {
WallConfig wallConfig = new WallConfig();
Map<String, Object> map = DsConfigUtil.mergeConfig(c, g);
Map<String, Object> map = DsConfigUtil.mergeMap(c, g);
Object dir = map.get("dir");
if (dir != null) {
wallConfig.loadConfig(String.valueOf(dir));

View File

@ -15,8 +15,8 @@
*/
package com.baomidou.dynamic.datasource.creator.hikaricp;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import lombok.Getter;
import lombok.Setter;
import java.util.Properties;
@ -26,8 +26,8 @@ import java.util.Properties;
* @author TaoYu
* @since 2.4.1
*/
@Data
@Slf4j
@Getter
@Setter
public class HikariCpConfig {
private String catalog;
@ -63,10 +63,20 @@ public class HikariCpConfig {
private Long keepaliveTime;
private Boolean sealed;
/**
* 设置 最大连接数 maxPoolSize
*
* @param maximumPoolSize 最大链接数
*/
public void setMaximumPoolSize(Integer maximumPoolSize) {
this.maxPoolSize = maximumPoolSize;
}
/**
* 设置 最小空闲连接数 minIdle
*
* @param minimumIdle 最小空闲连接数
*/
public void setMinimumIdle(Integer minimumIdle) {
this.minIdle = minimumIdle;
}

View File

@ -21,21 +21,46 @@ import lombok.Getter;
* 目前支持的XA数据源
*
* @author <a href="mailto:312290710@qq.com">jiazhifeng</a>
* @date 2023/03/02 23:05
*/
@Getter
public enum XADataSourceEnum {
/**
* mysql
*/
MYSQL("com.mysql.cj.jdbc.MysqlXADataSource"),
/**
* oracle
*/
ORACLE("oracle.jdbc.xa.client.OracleXADataSource"),
/**
* postgresql
*/
POSTGRE_SQL("org.postgresql.xa.PGXADataSource"),
/**
* h2
*/
H2("org.h2.jdbcx.JdbcDataSource");
/**
* xa数据源类名
*/
private final String xaDriverClassName;
/**
* 构造方法
*
* @param xaDriverClassName
*/
XADataSourceEnum(String xaDriverClassName) {
this.xaDriverClassName = xaDriverClassName;
}
/**
* 是否包含
*
* @param xaDataSourceClassName xa数据源类名
* @return boolean 包含
*/
public static boolean contains(String xaDataSourceClassName) {
for (XADataSourceEnum item : values()) {
if (item.getXaDriverClassName().equals(xaDataSourceClassName)) {

View File

@ -23,10 +23,21 @@ package com.baomidou.dynamic.datasource.exception;
*/
public class ErrorCreateDataSourceException extends RuntimeException {
/**
* Constructor for ErrorCreateDataSourceException.
*
* @param message message
*/
public ErrorCreateDataSourceException(String message) {
super(message);
}
/**
* Constructor for ErrorCreateDataSourceException.
*
* @param message message
* @param cause cause
*/
public ErrorCreateDataSourceException(String message, Throwable cause) {
super(message, cause);
}

View File

@ -1,18 +1,3 @@
/*
* Copyright © 2018 organization baomidou
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.dynamic.datasource.toolkit;
/**

View File

@ -49,7 +49,7 @@ public class ConfigMergeCreator<C, T> {
if (configClazz.equals(targetClazz) && global == null) {
return (T) item;
}
T result = targetClazz.newInstance();
T result = targetClazz.getDeclaredConstructor().newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(configClazz, Object.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : propertyDescriptors) {
@ -108,7 +108,7 @@ public class ConfigMergeCreator<C, T> {
} catch (ReflectiveOperationException e1) {
log.warn("dynamic-datasource set {} [{}] failed,please check your config or update {} to the latest version", configName, name, configName);
} finally {
if (field != null && field.isAccessible()) {
if (field != null) {
field.setAccessible(false);
}
}

View File

@ -1,18 +1,3 @@
/*
* Copyright © 2018 organization baomidou
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.dynamic.datasource.toolkit;
import javax.crypto.Cipher;
@ -52,15 +37,36 @@ public class CryptoUtils {
public static final String DEFAULT_PUBLIC_KEY_STRING = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ4o6sn4WoPmbs7DR9mGQzuuUQM9erQTVPpwxIzB0ETYkyKffO097qXVRLA6KPmaV+/siWewR7vpfYYjWajw5KkCAwEAAQ==";
private static final String DEFAULT_PRIVATE_KEY_STRING = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAnijqyfhag+ZuzsNH2YZDO65RAz16tBNU+nDEjMHQRNiTIp987T3updVEsDoo+ZpX7+yJZ7BHu+l9hiNZqPDkqQIDAQABAkBgErbczRIewWFaE+GXTymUHUV01Gmu7XdXUhzy6+CZkIcEnyTpUgPilGUydiIyeiY8usvWKGjFWxLoKeJDY1wBAiEA5M9uqc9XpL5uitLWHiiq7pRxhnJb/B+wZyHqLVhCLekCIQCw9D/Fsx7vHRgymWYExHvCka7w5SyWUmNzQOOKjZUIwQIhAMqbo7JaF5GZzui+qTsrZ7C7YYtb2Hf414t7TJG6hV+BAiBXuZ7r+fL6A+h9HUNQVcAtI2AhGNxT4aBgAOlNRQd/gQIgCGqaZsOdnL9624SI1DwhBt4x24q3350pWwzgfl4Kbbo=";
/**
* 加密算法RSA
*
* @param cipherText 需要加密的字符串
* @return 加密后的字符串
* @throws Exception 加密过程中的异常信息
*/
public static String decrypt(String cipherText) throws Exception {
return decrypt((String) null, cipherText);
}
/**
* 加密算法RSA
*
* @param publicKeyText 公钥
* @param cipherText 需要加密的字符串
* @return 加密后的字符串
* @throws Exception 加密过程中的异常信息
*/
public static String decrypt(String publicKeyText, String cipherText) throws Exception {
PublicKey publicKey = getPublicKey(publicKeyText);
return decrypt(publicKey, cipherText);
}
/**
* 加密算法RSA
*
* @param x509File 公钥文件
* @return 加密后的字符串
*/
public static PublicKey getPublicKeyByX509(String x509File) {
if (x509File == null || x509File.length() == 0) {
return getPublicKey(null);
@ -85,6 +91,12 @@ public class CryptoUtils {
}
}
/**
* 加密算法RSA
*
* @param publicKeyText 公钥
* @return 加密后的字符串
*/
public static PublicKey getPublicKey(String publicKeyText) {
if (publicKeyText == null || publicKeyText.length() == 0) {
publicKeyText = DEFAULT_PUBLIC_KEY_STRING;
@ -102,6 +114,12 @@ public class CryptoUtils {
}
}
/**
* 加密算法RSA
*
* @param publicKeyFile 公钥文件
* @return 加密后的字符串
*/
public static PublicKey getPublicKeyByPublicKeyFile(String publicKeyFile) {
if (publicKeyFile == null || publicKeyFile.length() == 0) {
return getPublicKey(null);
@ -134,6 +152,14 @@ public class CryptoUtils {
}
}
/**
* 加密算法RSA
*
* @param publicKey 公钥
* @param cipherText 需要加密的字符串
* @return 加密后的字符串
* @throws Exception 加密过程中的异常信息
*/
public static String decrypt(PublicKey publicKey, String cipherText) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
try {
@ -160,10 +186,25 @@ public class CryptoUtils {
return new String(plainBytes);
}
/**
* 加密算法RSA
*
* @param plainText 需要加密的字符串
* @return 加密后的字符串
* @throws Exception 加密过程中的异常信息
*/
public static String encrypt(String plainText) throws Exception {
return encrypt((String) null, plainText);
}
/**
* 加密算法RSA
*
* @param key 加密的密钥
* @param plainText 需要加密的字符串
* @return 加密后的字符串
* @throws Exception 加密过程中的异常信息
*/
public static String encrypt(String key, String plainText) throws Exception {
if (key == null) {
key = DEFAULT_PRIVATE_KEY_STRING;
@ -173,6 +214,14 @@ public class CryptoUtils {
return encrypt(keyBytes, plainText);
}
/**
* 加密算法RSA
*
* @param keyBytes 加密的密钥
* @param plainText 需要加密的字符串
* @return 加密后的字符串
* @throws Exception 加密过程中的异常信息
*/
public static String encrypt(byte[] keyBytes, String plainText) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA", "SunRsaSign");
@ -192,6 +241,12 @@ public class CryptoUtils {
return Base64.byteArrayToBase64(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)));
}
/**
* 生成密钥对
*
* @param keySize 密钥对长度
* @return 密钥对
*/
public static byte[][] genKeyPairBytes(int keySize) {
byte[][] keyPairBytes = new byte[2][];
try {
@ -208,6 +263,12 @@ public class CryptoUtils {
return keyPairBytes;
}
/**
* 生成密钥对
*
* @param keySize 密钥对长度
* @return Base64编码后的密钥对
*/
public static String[] genKeyPair(int keySize) {
byte[][] keyPairBytes = genKeyPairBytes(keySize);
String[] keyPairs = new String[2];

View File

@ -56,7 +56,7 @@ public final class DsConfigUtil {
* @param g 全局配置
* @return 合并配置
*/
public static Map<String, Object> mergeConfig(Map<String, Object> c, Map<String, Object> g) {
public static Map<String, Object> mergeMap(Map<String, Object> c, Map<String, Object> g) {
int size = 1 + (int) ((c.size() + g.size()) / 0.75);
Map<String, Object> map = new HashMap<>(size);
map.putAll(g);
@ -64,6 +64,19 @@ public final class DsConfigUtil {
return map;
}
public static Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class<?> clazz) {
Map<String, PropertyDescriptor> methodMap = new HashMap<>();
try {
for (PropertyDescriptor pd : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
if (!"class".equals(pd.getName())) {
methodMap.put(pd.getName(), pd);
}
}
} catch (Exception ignore) {
}
return methodMap;
}
/**
* 通过clazz获取对应的setter方法
*
@ -109,7 +122,7 @@ public final class DsConfigUtil {
*
* @param method 方法
* @param value
* @return
* @return 对应值
*/
public static Object convertValue(Method method, Object value) {
Class<?>[] parameterTypes = method.getParameterTypes();
@ -120,19 +133,19 @@ public final class DsConfigUtil {
return propertyValue;
}
if (parameterType == Integer.class || parameterType == int.class) {
return Integer.valueOf(propertyValue).intValue();
return Integer.valueOf(propertyValue);
}
if (parameterType == Long.class || parameterType == long.class) {
return Long.valueOf(propertyValue).longValue();
return Long.valueOf(propertyValue);
}
if (parameterType == Boolean.class || parameterType == boolean.class) {
return Boolean.valueOf(propertyValue).booleanValue();
return Boolean.valueOf(propertyValue);
}
if (parameterType == Double.class || parameterType == double.class) {
return Double.valueOf(propertyValue).doubleValue();
return Double.valueOf(propertyValue);
}
if (parameterType == Float.class || parameterType == float.class) {
return Float.valueOf(propertyValue).floatValue();
return Float.valueOf(propertyValue);
}
}
return value;

View File

@ -15,16 +15,39 @@
*/
package com.baomidou.dynamic.datasource.toolkit;
/**
* 常见字符串工具类
*
* @author TaoYu
*/
public abstract class DsStrUtils {
/**
* 判断字符串是否为空
*
* @param str 字符串
* @return true: null or "" or " " false: "a"
*/
public static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
}
/**
* 判断字符串是否为空
*
* @param str 字符串
* @return true: null or "" or " " false: "a"
*/
public static boolean hasLength(String str) {
return hasLength((CharSequence) str);
}
/**
* 判断字符串是否有内容
*
* @param str 字符串
* @return true: null or "" or " " false: "a"
*/
public static boolean hasText(CharSequence str) {
if (!hasLength(str)) {
return false;

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</parent>
<artifactId>dynamic-datasource-spring-boot-common</artifactId>

View File

@ -29,6 +29,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import java.util.LinkedHashMap;
import java.util.Map;
@ -86,26 +87,32 @@ public class DynamicDataSourceProperties {
/**
* Druid全局参数配置
*/
@NestedConfigurationProperty
private DruidConfig druid = new DruidConfig();
/**
* HikariCp全局参数配置
*/
@NestedConfigurationProperty
private HikariCpConfig hikari = new HikariCpConfig();
/**
* BeeCp全局参数配置
*/
@NestedConfigurationProperty
private BeeCpConfig beecp = new BeeCpConfig();
/**
* DBCP2全局参数配置
*/
@NestedConfigurationProperty
private Dbcp2Config dbcp2 = new Dbcp2Config();
/**
* atomikos全局参数配置
*/
@NestedConfigurationProperty
private AtomikosConfig atomikos = new AtomikosConfig();
/**
* aop with default ds annotation
*/
@NestedConfigurationProperty
private DynamicDatasourceAopProperties aop = new DynamicDatasourceAopProperties();
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</parent>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
@ -19,6 +19,12 @@
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-common</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>

View File

@ -0,0 +1,31 @@
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidFilterConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidStatViewServletConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidWebStatFilterConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 从原生DruidDataSourceAutoConfigure复制
*
* @author TaoYu
* @since 1.1.0
*/
@Configuration
@ConditionalOnClass(DruidDataSourceAutoConfigure.class)
@EnableConfigurationProperties({DruidStatProperties.class})
@Import({
DruidSpringAopConfiguration.class,
DruidStatViewServletConfiguration.class,
DruidWebStatFilterConfiguration.class,
DruidFilterConfiguration.class})
public class DruidDynamicDataSourceConfiguration {
}

View File

@ -47,7 +47,7 @@ import java.util.List;
@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
@AutoConfigureBefore(value = DataSourceAutoConfiguration.class, name = "com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure")
@Import(value = {DynamicDataSourceCreatorAutoConfiguration.class, DynamicDataSourceAopConfiguration.class, DynamicDataSourceAssistConfiguration.class})
@Import({DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class, DynamicDataSourceAopConfiguration.class, DynamicDataSourceAssistConfiguration.class})
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class DynamicDataSourceAutoConfiguration implements InitializingBean {

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</parent>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
@ -25,6 +25,12 @@
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-common</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.18</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>

View File

@ -0,0 +1,30 @@
package com.baomidou.dynamic.datasource.spring.boot.autoconfigure;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidFilterConfiguration;
import com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidSpringAopConfiguration;
import com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidStatViewServletConfiguration;
import com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidWebStatFilterConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 从原生DruidDataSourceAutoConfigure复制
*
* @author TaoYu
* @since 1.1.0
*/
@Configuration
@ConditionalOnClass(DruidDataSourceAutoConfigure.class)
@EnableConfigurationProperties({DruidStatProperties.class})
@Import({
DruidSpringAopConfiguration.class,
DruidStatViewServletConfiguration.class,
DruidWebStatFilterConfiguration.class,
DruidFilterConfiguration.class})
public class DruidDynamicDataSourceConfiguration {
}

View File

@ -41,8 +41,13 @@ import java.util.List;
@Slf4j
@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
@AutoConfigureBefore(value = DataSourceAutoConfiguration.class, name = "com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure")
@Import(value = {DynamicDataSourceCreatorAutoConfiguration.class, DynamicDataSourceAopConfiguration.class, DynamicDataSourceAssistConfiguration.class})
@AutoConfigureBefore(
value = DataSourceAutoConfiguration.class,
name = {
"com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure",
"com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure"
})
@Import({DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class, DynamicDataSourceAopConfiguration.class, DynamicDataSourceAssistConfiguration.class})
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class DynamicDataSourceAutoConfiguration implements InitializingBean {

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</parent>
<artifactId>dynamic-datasource-spring</artifactId>
<url>https://github.com/baomidou/dynamic-datasource-spring-boot-starter/tree/master/dynamic-datasource-spring</url>

View File

@ -28,9 +28,25 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DSTransactional {
/**
* 回滚异常
*
* @return Class[]
*/
Class<? extends Throwable>[] rollbackFor() default {Exception.class};
/**
* 不回滚异常
*
* @return Class[]
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 事务传播行为
*
* @return DsPropagation
*/
DsPropagation propagation() default DsPropagation.REQUIRED;
}

View File

@ -37,17 +37,33 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* DynamicDataSource Annotation Pointcut
*
* @author TaoYu
* @since 1.2.0
*/
public class DynamicDataSourceAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
/**
* the advice
*/
private final Advice advice;
/**
* the pointcut
*/
private final Pointcut pointcut;
/**
* the annotation
*/
private final Class<? extends Annotation> annotation;
/**
* 构造方法
*
* @param advice 切面
* @param annotation 注解
*/
public DynamicDataSourceAnnotationAdvisor(@NonNull MethodInterceptor advice,
@NonNull Class<? extends Annotation> annotation) {
this.advice = advice;

View File

@ -37,6 +37,12 @@ public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor
private final DataSourceClassResolver dataSourceClassResolver;
private final DsProcessor dsProcessor;
/**
* init
*
* @param allowedPublicOnly allowedPublicOnly
* @param dsProcessor dsProcessor
*/
public DynamicDataSourceAnnotationInterceptor(Boolean allowedPublicOnly, DsProcessor dsProcessor) {
dataSourceClassResolver = new DataSourceClassResolver(allowedPublicOnly);
this.dsProcessor = dsProcessor;
@ -53,6 +59,12 @@ public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor
}
}
/**
* Determine the key of the datasource
*
* @param invocation MethodInvocation
* @return dsKey
*/
private String determineDatasourceKey(MethodInvocation invocation) {
String key = dataSourceClassResolver.findKey(invocation.getMethod(), invocation.getThis());
return key.startsWith(DYNAMIC_PREFIX) ? dsProcessor.determineDatasource(invocation, key) : key;

View File

@ -45,6 +45,11 @@ public class DynamicDatasourceNamedInterceptor implements MethodInterceptor {
private final Map<String, String> nameMap = new HashMap<>();
private final DsProcessor dsProcessor;
/**
* init
*
* @param dsProcessor dsProcessor
*/
public DynamicDatasourceNamedInterceptor(DsProcessor dsProcessor) {
this.dsProcessor = dsProcessor;
}

View File

@ -26,6 +26,8 @@ import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;
/**
* Dynamic DataSource Transaction Interceptor
*
* @author funkye
*/
@Slf4j

View File

@ -66,6 +66,12 @@ public class DefaultDataSourceCreator {
private DataSourceInitEvent dataSourceInitEvent;
/**
* 创建数据源
*
* @param dataSourceProperty 数据源参数
* @return 数据源
*/
public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
DataSourceCreator dataSourceCreator = null;
for (DataSourceCreator creator : this.creators) {
@ -96,6 +102,12 @@ public class DefaultDataSourceCreator {
return wrapDataSource(dataSource, dataSourceProperty);
}
/**
* 执行初始化脚本
*
* @param dataSource 数据源
* @param dataSourceProperty 数据源参数
*/
private void runScrip(DataSource dataSource, DataSourceProperty dataSourceProperty) {
DatasourceInitProperties initProperty = dataSourceProperty.getInit();
String schema = initProperty.getSchema();
@ -111,6 +123,13 @@ public class DefaultDataSourceCreator {
}
}
/**
* 包装数据源
*
* @param dataSource 数据源
* @param dataSourceProperty 数据源参数
* @return
*/
private DataSource wrapDataSource(DataSource dataSource, DataSourceProperty dataSourceProperty) {
String name = dataSourceProperty.getPoolName();
DataSource targetDataSource = dataSource;

View File

@ -31,16 +31,16 @@ public class JndiDataSourceCreator implements DataSourceCreator {
private static final JndiDataSourceLookup LOOKUP = new JndiDataSourceLookup();
/**
* 创建JNDI数据源
*
* @param jndiName jndi数据源名称
* @return 数据源
*/
public DataSource createDataSource(String jndiName) {
return LOOKUP.getDataSource(jndiName);
}
/**
* 创建JNDI数据源
*
* @param dataSourceProperty jndi数据源名称
* @return 数据源
*/
@Override
public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
return createDataSource(dataSourceProperty.getJndiName());

View File

@ -20,11 +20,16 @@ import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.logging.Logger;
/**
* Abstract base class for Spring's {@link javax.sql.DataSource}
*/
public abstract class AbstractDataSource implements DataSource {
/**
* Returns 0, indicating the default system timeout is to be used.
*
* @return 0
*/
@Override
public int getLoginTimeout() throws SQLException {
@ -32,7 +37,8 @@ public abstract class AbstractDataSource implements DataSource {
}
/**
* Setting a login timeout is not supported.
* @param timeout ignored
* <p>Throws {@link UnsupportedOperationException} when called.
*/
@Override
public void setLoginTimeout(int timeout) throws SQLException {
@ -41,6 +47,9 @@ public abstract class AbstractDataSource implements DataSource {
/**
* LogWriter methods are not supported.
*
* @return not supported
* <p>Throws {@link UnsupportedOperationException} when called.
*/
@Override
public PrintWriter getLogWriter() {
@ -49,6 +58,7 @@ public abstract class AbstractDataSource implements DataSource {
/**
* LogWriter methods are not supported.
* <p>Throws {@link UnsupportedOperationException} when called.
*/
@Override
public void setLogWriter(PrintWriter pw) throws SQLException {

View File

@ -31,6 +31,9 @@ import java.util.concurrent.ConcurrentHashMap;
@Data
public class GroupDataSource {
/**
* 组名
*/
private String groupName;
private DynamicDataSourceStrategy dynamicDataSourceStrategy;
@ -47,6 +50,7 @@ public class GroupDataSource {
*
* @param ds the name of the datasource
* @param dataSource datasource
* @return the previous value associated with ds, or null if there was no mapping for ds.
*/
public DataSource addDatasource(String ds, DataSource dataSource) {
return dataSourceMap.put(ds, dataSource);
@ -59,14 +63,29 @@ public class GroupDataSource {
return dataSourceMap.remove(ds);
}
/**
* determineDsKey
*
* @return the name of the datasource
*/
public String determineDsKey() {
return dynamicDataSourceStrategy.determineKey(new ArrayList<>(dataSourceMap.keySet()));
}
/**
* determineDataSource
*
* @return the datasource
*/
public DataSource determineDataSource() {
return dataSourceMap.get(determineDsKey());
}
/**
* size of this group
*
* @return the size of this group
*/
public int size() {
return dataSourceMap.size();
}

View File

@ -23,10 +23,21 @@ package com.baomidou.dynamic.datasource.exception;
*/
public class CannotFindDataSourceException extends RuntimeException {
/**
* 构造方法
*
* @param message 异常信息
*/
public CannotFindDataSourceException(String message) {
super(message);
}
/**
* 构造方法
*
* @param message 异常信息
* @param cause 异常
*/
public CannotFindDataSourceException(String message, Throwable cause) {
super(message, cause);
}

View File

@ -15,11 +15,27 @@
*/
package com.baomidou.dynamic.datasource.exception;
/**
* 事务异常
*
* @author Hzh
*/
public class TransactionException extends RuntimeException {
/**
* 构造
*
* @param message 消息
*/
public TransactionException(String message) {
super(message);
}
/**
* 构造
*
* @param message 消息
* @param cause 异常
*/
public TransactionException(String message, Throwable cause) {
super(message, cause);
}

View File

@ -26,9 +26,7 @@ import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sql.DataSource;
import java.util.Properties;
/**
@ -44,9 +42,6 @@ import java.util.Properties;
@Slf4j
public class MasterSlaveAutoRoutingPlugin implements Interceptor {
@Autowired
protected DataSource dynamicDataSource;
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();

View File

@ -18,13 +18,23 @@ package com.baomidou.dynamic.datasource.processor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 数据源处理器
*
* @author TaoYu
* @since 2.5.0
*/
public abstract class DsProcessor {
/**
* 下一个执行器
*/
private DsProcessor nextProcessor;
/**
* 设置下一个执行器
*
* @param dsProcessor 执行器
*/
public void setNextProcessor(DsProcessor dsProcessor) {
this.nextProcessor = dsProcessor;
}

View File

@ -30,6 +30,8 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
/**
* SpEL表达式处理器
*
* @author TaoYu
* @since 2.5.0
*/
@ -84,10 +86,20 @@ public class DsSpelExpressionProcessor extends DsProcessor {
return value == null ? null : value.toString();
}
/**
* 设置解析上下文
*
* @param parserContext 解析上下文
*/
public void setParserContext(ParserContext parserContext) {
this.parserContext = parserContext;
}
/**
* 设置bean解析器
*
* @param beanResolver bean解析器
*/
public void setBeanResolver(BeanResolver beanResolver) {
this.beanResolver = beanResolver;
}

View File

@ -33,6 +33,12 @@ public abstract class AbstractDataSourceProvider implements DynamicDataSourcePro
private final DefaultDataSourceCreator defaultDataSourceCreator;
/**
* 创建数据源
*
* @param dataSourcePropertiesMap 数据源参数Map
* @return 数据源Map
*/
protected Map<String, DataSource> createDataSourceMap(
Map<String, DataSourceProperty> dataSourcePropertiesMap) {
Map<String, DataSource> dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size() * 2);

View File

@ -53,10 +53,27 @@ public abstract class AbstractJdbcDataSourceProvider extends AbstractDataSourceP
*/
private final String password;
/**
* 通过默认数据源创建器创建数据源
*
* @param defaultDataSourceCreator 默认数据源创建器
* @param url 数据源url
* @param username 用户名
* @param password 密码
*/
public AbstractJdbcDataSourceProvider(DefaultDataSourceCreator defaultDataSourceCreator, String url, String username, String password) {
this(defaultDataSourceCreator, null, url, username, password);
}
/**
* 通过默认数据源创建器创建数据源
*
* @param defaultDataSourceCreator 默认数据源创建器
* @param driverClassName 驱动类名
* @param url 数据源url
* @param username 用户名
* @param password 密码
*/
public AbstractJdbcDataSourceProvider(DefaultDataSourceCreator defaultDataSourceCreator, String driverClassName, String url, String username, String password) {
super(defaultDataSourceCreator);
this.driverClassName = driverClassName;
@ -65,6 +82,11 @@ public abstract class AbstractJdbcDataSourceProvider extends AbstractDataSourceP
this.password = password;
}
/**
* 关闭资源
*
* @param con 资源
*/
private static void closeResource(AutoCloseable con) {
if (con != null) {
try {

View File

@ -36,6 +36,12 @@ public class YmlDynamicDataSourceProvider extends AbstractDataSourceProvider {
*/
private final Map<String, DataSourceProperty> dataSourcePropertiesMap;
/**
* 构造函数
*
* @param defaultDataSourceCreator 默认数据源创建器
* @param dataSourcePropertiesMap 数据源参数
*/
public YmlDynamicDataSourceProvider(DefaultDataSourceCreator defaultDataSourceCreator, Map<String, DataSourceProperty> dataSourcePropertiesMap) {
super(defaultDataSourceCreator);
this.dataSourcePropertiesMap = dataSourcePropertiesMap;

View File

@ -63,6 +63,7 @@ public final class DynamicDataSourceContextHolder {
* </p>
*
* @param ds 数据源名称
* @return 数据源名称
*/
public static String push(String ds) {
String dataSourceStr = StringUtils.isEmpty(ds) ? "" : ds;

View File

@ -27,7 +27,6 @@ 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 {

View File

@ -31,7 +31,9 @@ import java.util.concurrent.ConcurrentHashMap;
* @author funkye zp
*/
public class ConnectionFactory {
/**
* connection holder
*/
private static final ThreadLocal<Map<String, Map<String, ConnectionProxy>>> CONNECTION_HOLDER =
new ThreadLocal<Map<String, Map<String, ConnectionProxy>>>() {
@Override
@ -39,6 +41,9 @@ public class ConnectionFactory {
return new ConcurrentHashMap<>();
}
};
/**
* savepoint connection holder
*/
private static final ThreadLocal<Map<String, List<SavePointHolder>>> SAVEPOINT_CONNECTION_HOLDER =
new ThreadLocal<Map<String, List<SavePointHolder>>>() {
@Override
@ -47,6 +52,13 @@ public class ConnectionFactory {
}
};
/**
* put connection
*
* @param xid xid
* @param ds ds
* @param connection connection
*/
public static void putConnection(String xid, String ds, ConnectionProxy connection) {
Map<String, Map<String, ConnectionProxy>> concurrentHashMap = CONNECTION_HOLDER.get();
Map<String, ConnectionProxy> connectionProxyMap = concurrentHashMap.get(xid);
@ -64,6 +76,13 @@ public class ConnectionFactory {
}
}
/**
* getConnection
*
* @param xid 事务ID
* @param ds ds
* @return boolean
*/
public static ConnectionProxy getConnection(String xid, String ds) {
Map<String, Map<String, ConnectionProxy>> concurrentHashMap = CONNECTION_HOLDER.get();
Map<String, ConnectionProxy> connectionProxyMap = concurrentHashMap.get(xid);
@ -73,6 +92,13 @@ public class ConnectionFactory {
return connectionProxyMap.get(ds);
}
/**
* Whether there is a savepoint
*
* @param xid xid
* @param state state
* @throws Exception Exception
*/
public static void notify(String xid, Boolean state) throws Exception {
Exception exception = null;
Map<String, Map<String, ConnectionProxy>> concurrentHashMap = CONNECTION_HOLDER.get();
@ -143,6 +169,12 @@ public class ConnectionFactory {
}
}
/**
* Whether there is a savepoint
*
* @param xid 事务ID
* @throws TransactionException TransactionException
*/
public static void createSavepoint(String xid) throws TransactionException {
try {
Map<String, List<SavePointHolder>> savePointMap = SAVEPOINT_CONNECTION_HOLDER.get();
@ -180,6 +212,12 @@ public class ConnectionFactory {
}
/**
* Determine whether there is a savepoint
*
* @param xid 事务ID
* @return true or false
*/
public static boolean hasSavepoint(String xid) {
Map<String, List<SavePointHolder>> savePointMap = SAVEPOINT_CONNECTION_HOLDER.get();
return !CollectionUtils.isEmpty(savePointMap.get(xid));

View File

@ -15,6 +15,8 @@
*/
package com.baomidou.dynamic.datasource.tx;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.sql.*;
@ -27,6 +29,8 @@ import java.util.concurrent.Executor;
* @author funkye
*/
@Slf4j
@Getter
@Setter
public class ConnectionProxy implements Connection {
private Connection connection;
@ -35,11 +39,23 @@ public class ConnectionProxy implements Connection {
private int savepointCounter = 0;
/**
* init
*
* @param connection 数据源链接
* @param ds 数据源名称
*/
public ConnectionProxy(Connection connection, String ds) {
this.connection = connection;
this.ds = ds;
}
/**
* 通知事务管理器提交或回滚
*
* @param commit 是否提交
* @throws SQLException SQLException
*/
public void notify(Boolean commit) throws SQLException {
try {
if (commit) {
@ -349,28 +365,4 @@ public class ConnectionProxy implements Connection {
public int hashCode() {
return Objects.hash(connection, ds);
}
public Connection getConnection() {
return connection;
}
public void setConnection(Connection connection) {
this.connection = connection;
}
public String getDs() {
return ds;
}
public void setDs(String ds) {
this.ds = ds;
}
public int getSavepointCounter() {
return savepointCounter;
}
public void setSavepointCounter(int savepointCounter) {
this.savepointCounter = savepointCounter;
}
}

View File

@ -18,18 +18,32 @@ package com.baomidou.dynamic.datasource.tx;
public enum DsPropagation {
//支持当前事务如果当前没有事务就新建一个事务这是最常见的选择
/**
* 支持当前事务如果当前没有事务就新建一个事务这是最常见的选择
*/
REQUIRED,
//新建事务如果当前存在事务把当前事务挂起
/**
* 新建事务如果当前存在事务把当前事务挂起
*/
REQUIRES_NEW,
//以非事务方式执行操作如果当前存在事务就把当前事务挂起
/**
* 以非事务方式执行操作如果当前存在事务就把当前事务挂起
*/
NOT_SUPPORTED,
//支持当前事务如果当前没有事务就以非事务方式执行
/**
* 支持当前事务如果当前没有事务就以非事务方式执行
*/
SUPPORTS,
//以非事务方式执行如果当前存在事务则抛出异常
/**
* 以非事务方式执行如果当前存在事务则抛出异常
*/
NEVER,
//支持当前事务如果当前没有事务就抛出异常
/**
* 支持当前事务如果当前没有事务就抛出异常
*/
MANDATORY,
//如果当前存在事务则在嵌套事务内执行如果当前没有事务就新建一个事务
/**
* 如果当前存在事务则在嵌套事务内执行如果当前没有事务就新建一个事务
*/
NESTED
}

View File

@ -21,7 +21,6 @@ import org.springframework.util.StringUtils;
import java.security.SecureRandom;
import java.util.UUID;
/**
* 本地事务工具类
*
@ -30,6 +29,10 @@ import java.util.UUID;
*/
@Slf4j
public final class LocalTxUtil {
/**
* SecureRandom instance used to generate UUIDs.
*/
private static final ThreadLocal<SecureRandom> SECURE_RANDOM_HOLDER = new ThreadLocal<SecureRandom>() {
@Override
protected SecureRandom initialValue() {
@ -37,6 +40,11 @@ public final class LocalTxUtil {
}
};
/**
* 随机生成UUID
*
* @return UUID
*/
public static UUID randomUUID() {
SecureRandom ng = SECURE_RANDOM_HOLDER.get();
byte[] randomBytes = new byte[16];
@ -62,6 +70,8 @@ public final class LocalTxUtil {
/**
* 手动开启事务
*
* @return 事务ID
*/
public static String startTransaction() {
String xid = TransactionContext.getXID();
@ -77,6 +87,8 @@ public final class LocalTxUtil {
/**
* 手动提交事务
*
* @param xid 事务ID
*/
public static void commit(String xid) throws Exception {
boolean hasSavepoint = ConnectionFactory.hasSavepoint(xid);
@ -92,6 +104,8 @@ public final class LocalTxUtil {
/**
* 手动回滚事务
*
* @param xid 事务ID
*/
public static void rollback(String xid) throws Exception {
boolean hasSavepoint = ConnectionFactory.hasSavepoint(xid);

View File

@ -23,18 +23,39 @@ import java.util.LinkedList;
import java.util.List;
/**
* Savepoint Holder
*
* @author zp
*/
public class SavePointHolder {
/**
* savepoint name prefix
*/
private static final String SAVEPOINT_NAME_PREFIX = "DYNAMIC_";
/**
* connection proxy
*/
private ConnectionProxy connectionProxy;
/**
* savepoints
*/
private LinkedList<Savepoint> savepoints;
/**
* constructor
*
* @param connectionProxy connection proxy
*/
public SavePointHolder(ConnectionProxy connectionProxy) {
this.connectionProxy = connectionProxy;
this.savepoints = new LinkedList<>();
}
/**
* conversion savepoint holder
*
* @throws SQLException SQLException
*/
public void conversionSavePointHolder() throws SQLException {
if (connectionProxy == null) {
throw new SQLTransientConnectionException();
@ -45,6 +66,12 @@ public class SavePointHolder {
savepoints.addLast(savepoint);
}
/**
* release savepoint
*
* @return savepoint index
* @throws SQLException SQLException
*/
public int releaseSavepoint() throws SQLException {
Savepoint savepoint = savepoints.pollLast();
if (savepoint != null) {
@ -55,6 +82,12 @@ public class SavePointHolder {
return -1;
}
/**
* rollback savepoint
*
* @return savepoint index
* @throws SQLException SQLException
*/
public int rollbackSavePoint() throws SQLException {
Savepoint savepoint = savepoints.pollLast();
if (savepoint != null) {
@ -65,10 +98,20 @@ public class SavePointHolder {
return -1;
}
/**
* get connection proxy
*
* @return
*/
public ConnectionProxy getConnectionProxy() {
return this.connectionProxy;
}
/**
* get savepoints
*
* @return
*/
public List<Savepoint> getSavePoints() {
return this.savepoints;
}

View File

@ -15,14 +15,22 @@
*/
package com.baomidou.dynamic.datasource.tx;
import javax.annotation.Nonnull;
/**
* SuspendedResourcesHolder
*
* @author Hzh
*/
public class SuspendedResourcesHolder {
/**
* The xid
* 事务ID
*/
private String xid;
/**
* Instantiates a new Suspended resources holder.
*
* @param xid 事务ID
*/
public SuspendedResourcesHolder(String xid) {
if (xid == null) {
throw new IllegalArgumentException("xid must be not null");
@ -30,7 +38,11 @@ public class SuspendedResourcesHolder {
this.xid = xid;
}
@Nonnull
/**
* 获得事务ID.
*
* @return 事务ID
*/
public String getXid() {
return xid;
}

View File

@ -27,7 +27,7 @@ public class TransactionContext {
/**
* Gets xid.
*
* @return the xid
* @return 事务ID
*/
public static String getXID() {
String xid = CONTEXT_HOLDER.get();
@ -40,6 +40,7 @@ public class TransactionContext {
/**
* Unbind string.
*
* @param xid 事务ID
* @return the string
*/
public static String unbind(String xid) {
@ -48,8 +49,9 @@ public class TransactionContext {
}
/**
* bind string.
* bind xid.
*
* @param xid 事务ID
* @return the string
*/
public static String bind(String xid) {

View File

@ -15,10 +15,25 @@
*/
package com.baomidou.dynamic.datasource.tx;
/**
* 事务执行器
*
* @author Hzh
*/
public interface TransactionalExecutor {
/**
* 执行
*
* @return object
* @throws Throwable Throwable
*/
Object execute() throws Throwable;
/**
* 获取事务信息
*
* @return TransactionalInfo
*/
TransactionalInfo getTransactionInfo();
}

View File

@ -15,35 +15,30 @@
*/
package com.baomidou.dynamic.datasource.tx;
import lombok.Getter;
import lombok.Setter;
/**
* 事务基础信息
*
* @author Hzh
*/
@Getter
@Setter
public class TransactionalInfo {
/**
* 回滚异常
*/
Class<? extends Throwable>[] rollbackFor;
/**
* 不回滚异常
*/
Class<? extends Throwable>[] noRollbackFor;
/**
* 事务传播行为
*/
DsPropagation propagation;
public Class<? extends Throwable>[] getRollbackFor() {
return rollbackFor;
}
public void setRollbackFor(Class<? extends Throwable>[] rollbackFor) {
this.rollbackFor = rollbackFor;
}
public Class<? extends Throwable>[] getNoRollbackFor() {
return noRollbackFor;
}
public void setNoRollbackFor(Class<? extends Throwable>[] noRollbackFor) {
this.noRollbackFor = noRollbackFor;
}
public DsPropagation getPropagation() {
return propagation;
}
public void setPropagation(DsPropagation propagation) {
this.propagation = propagation;
}
}

View File

@ -22,9 +22,21 @@ import org.springframework.util.StringUtils;
import java.util.Objects;
/**
* 事务模板
*
* @author Hzh
*/
@Slf4j
public class TransactionalTemplate {
/**
* Execute with transaction.
*
* @param transactionalExecutor TransactionalExecutor
* @return Object
* @throws Throwable Throwable
*/
public Object execute(TransactionalExecutor transactionalExecutor) throws Throwable {
TransactionalInfo transactionInfo = transactionalExecutor.getTransactionInfo();
DsPropagation propagation = transactionInfo.propagation;
@ -85,6 +97,13 @@ public class TransactionalTemplate {
}
}
/**
* 判断是否存在事务
*
* @param transactionalExecutor TransactionalExecutor
* @return 是否存在事务
* @throws Throwable Throwable
*/
private Object doExecute(TransactionalExecutor transactionalExecutor) throws Throwable {
TransactionalInfo transactionInfo = transactionalExecutor.getTransactionInfo();
DsPropagation propagation = transactionInfo.propagation;
@ -109,6 +128,13 @@ public class TransactionalTemplate {
return o;
}
/**
* 判断是否回滚
*
* @param e 异常
* @param transactionInfo 事务信息
* @return 是否回滚
*/
private boolean isRollback(Throwable e, TransactionalInfo transactionInfo) {
boolean isRollback = true;
Class<? extends Throwable>[] rollbacks = transactionInfo.rollbackFor;
@ -132,6 +158,13 @@ public class TransactionalTemplate {
return false;
}
/**
* 获取深度
*
* @param exceptionClass 异常类
* @param rollback 回滚类
* @return 深度
*/
private int getDepth(Class<?> exceptionClass, Class<? extends Throwable> rollback) {
if (rollback == Throwable.class || rollback == Exception.class) {
return 0;
@ -153,6 +186,11 @@ public class TransactionalTemplate {
}
}
/**
* 挂起资源
*
* @return 挂起资源
*/
public SuspendedResourcesHolder suspend() {
String xid = TransactionContext.getXID();
if (xid != null) {
@ -166,6 +204,11 @@ public class TransactionalTemplate {
}
}
/**
* 判断是否存在事务
*
* @return 是否存在事务
*/
public boolean existingTransaction() {
return !StringUtils.isEmpty(TransactionContext.getXID());
}

13
pom.xml
View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>dynamic datasource</description>
@ -78,17 +78,17 @@
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-creator</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-common</artifactId>
<version>4.1.1</version>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -211,6 +211,11 @@
<includes>
<include>src/main/java/**</include>
</includes>
<excludes>
<exclude>src/main/java/com/**/**/Base64.java</exclude>
<exclude>src/main/java/com/**/**/CryptoUtils.java</exclude>
<exclude>src/main/java/com/**/**/DruidDynamicDataSourceConfiguration.java</exclude>
</excludes>
</configuration>
<executions>
<execution>