featL 完善druid3兼容,完善javadoc
This commit is contained in:
parent
f5541d2b4e
commit
68a090fc99
77
README.md
77
README.md
@ -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");
|
||||
}
|
||||
}
|
||||
```
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ import org.aopalliance.intercept.MethodInvocation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Dynamic DataSource Transaction Interceptor
|
||||
*
|
||||
* @author funkye
|
||||
*/
|
||||
@Slf4j
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -63,6 +63,7 @@ public final class DynamicDataSourceContextHolder {
|
||||
* </p>
|
||||
*
|
||||
* @param ds 数据源名称
|
||||
* @return 数据源名称
|
||||
*/
|
||||
public static String push(String ds) {
|
||||
String dataSourceStr = StringUtils.isEmpty(ds) ? "" : ds;
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -18,18 +18,32 @@ package com.baomidou.dynamic.datasource.tx;
|
||||
|
||||
|
||||
public enum DsPropagation {
|
||||
//支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
|
||||
/**
|
||||
* 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
|
||||
*/
|
||||
REQUIRED,
|
||||
//新建事务,如果当前存在事务,把当前事务挂起。
|
||||
/**
|
||||
* 新建事务,如果当前存在事务,把当前事务挂起。
|
||||
*/
|
||||
REQUIRES_NEW,
|
||||
//以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
|
||||
/**
|
||||
* 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
|
||||
*/
|
||||
NOT_SUPPORTED,
|
||||
//支持当前事务,如果当前没有事务,就以非事务方式执行。
|
||||
/**
|
||||
* 支持当前事务,如果当前没有事务,就以非事务方式执行。
|
||||
*/
|
||||
SUPPORTS,
|
||||
//以非事务方式执行,如果当前存在事务,则抛出异常。
|
||||
/**
|
||||
* 以非事务方式执行,如果当前存在事务,则抛出异常。
|
||||
*/
|
||||
NEVER,
|
||||
//支持当前事务,如果当前没有事务,就抛出异常。
|
||||
/**
|
||||
* 支持当前事务,如果当前没有事务,就抛出异常。
|
||||
*/
|
||||
MANDATORY,
|
||||
//如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,就新建一个事务。
|
||||
/**
|
||||
* 如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,就新建一个事务。
|
||||
*/
|
||||
NESTED
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
13
pom.xml
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user