This commit is contained in:
Looly 2023-12-19 02:45:22 +08:00
parent 4b83504e50
commit 494c70b9e6
27 changed files with 796 additions and 1032 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Copyright (c) 2023. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
@ -10,22 +10,24 @@
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.exception;
package org.dromara.hutool.core.pool;
import org.dromara.hutool.core.exception.HutoolException;
/**
* 未初始化异常
* 对象池异常
*
* @author Looly
*/
public class NotInitedException extends HutoolException {
private static final long serialVersionUID = 8247610319171014183L;
public class PoolException extends HutoolException {
private static final long serialVersionUID = 1L;
/**
* 构造
*
* @param e 异常
*/
public NotInitedException(final Throwable e) {
public PoolException(final Throwable e) {
super(e);
}
@ -34,7 +36,7 @@ public class NotInitedException extends HutoolException {
*
* @param message 消息
*/
public NotInitedException(final String message) {
public PoolException(final String message) {
super(message);
}
@ -44,7 +46,7 @@ public class NotInitedException extends HutoolException {
* @param messageTemplate 消息模板
* @param params 参数
*/
public NotInitedException(final String messageTemplate, final Object... params) {
public PoolException(final String messageTemplate, final Object... params) {
super(messageTemplate, params);
}
@ -54,7 +56,7 @@ public class NotInitedException extends HutoolException {
* @param message 消息
* @param cause 被包装的子异常
*/
public NotInitedException(final String message, final Throwable cause) {
public PoolException(final String message, final Throwable cause) {
super(message, cause);
}
@ -66,7 +68,7 @@ public class NotInitedException extends HutoolException {
* @param enableSuppression 是否启用抑制
* @param writableStackTrace 堆栈跟踪是否应该是可写的
*/
public NotInitedException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
public PoolException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
@ -77,7 +79,7 @@ public class NotInitedException extends HutoolException {
* @param messageTemplate 消息模板
* @param params 参数
*/
public NotInitedException(final Throwable cause, final String messageTemplate, final Object... params) {
public PoolException(final Throwable cause, final String messageTemplate, final Object... params) {
super(cause, messageTemplate, params);
}
}

View File

@ -12,11 +12,7 @@
package org.dromara.hutool.core.pool.partition;
import org.dromara.hutool.core.exception.HutoolException;
import org.dromara.hutool.core.pool.ObjectFactory;
import org.dromara.hutool.core.pool.ObjectPool;
import org.dromara.hutool.core.pool.PoolConfig;
import org.dromara.hutool.core.pool.Poolable;
import org.dromara.hutool.core.pool.*;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
@ -99,7 +95,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
poolable = waitingPoll();
if (null == poolable) {
// 池空间达到最大值但是无可用对象
throw new HutoolException("Pool exhausted!");
throw new PoolException("Pool exhausted!");
}
}
@ -122,7 +118,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
try {
this.queue.put(poolable);
} catch (final InterruptedException e) {
throw new HutoolException(e);
throw new PoolException(e);
}
} else {
// 对象不可用
@ -150,7 +146,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
}
total += increaseSize;
} catch (final InterruptedException e) {
throw new HutoolException(e);
throw new PoolException(e);
}
return increaseSize;
}
@ -208,9 +204,9 @@ public class PoolPartition<T> implements ObjectPool<T> {
* 等待的时间取决于{@link PoolConfig#getMaxWait()}小于等于0时一直等待否则等待给定毫秒数
*
* @return 取出的池对象
* @throws HutoolException 中断异常
* @throws PoolException 中断异常
*/
private Poolable<T> waitingPoll() throws HutoolException {
private Poolable<T> waitingPoll() throws PoolException {
final long maxWait = this.config.getMaxWait();
try {
if (maxWait <= 0) {
@ -218,7 +214,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
}
return this.queue.poll(maxWait, TimeUnit.MILLISECONDS);
} catch (final InterruptedException e) {
throw new HutoolException(e);
throw new PoolException(e);
}
}
}

View File

@ -1325,7 +1325,7 @@ public class CharSequenceUtil extends StrValidator {
* @see #appendIfMissing(CharSequence, CharSequence, CharSequence...)
*/
public static String addSuffixIfNot(final CharSequence str, final CharSequence suffix) {
return appendIfMissing(str, suffix, suffix);
return appendIfMissing(str, suffix);
}
// endregion

View File

@ -1,224 +0,0 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db.ds;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.map.SafeConcurrentHashMap;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.DbUtil;
import org.dromara.hutool.db.GlobalDbConfig;
import org.dromara.hutool.db.driver.DriverUtil;
import org.dromara.hutool.setting.Setting;
import javax.sql.DataSource;
import java.util.Collection;
import java.util.Map;
/**
* 抽象数据源工厂<br>
* 此工厂抽象类用于实现数据源的缓存当用户多次调用{@link #getDataSource(String)} 工厂只需创建一次即可<br>
* 数据源是与配置文件中的分组相关的每个分组的数据源相互独立也就是每个分组的数据源是单例存在的
*
* @author looly
*/
public abstract class AbstractDSFactory implements DSFactory {
private static final long serialVersionUID = -6407302276272379881L;
/**
* 数据源名
*/
protected final String dataSourceName;
/**
* 数据库连接配置文件
*/
private final Setting setting;
/**
* 数据源池
*/
private final Map<String, DSWrapper> dsMap;
/**
* 构造
*
* @param dataSourceName 数据源名称
* @param dataSourceClass 数据库连接池实现类用于检测所提供的DataSource类是否存在当传入的DataSource类不存在时抛出ClassNotFoundException<br>
* 此参数的作用是在detectDSFactory方法自动检测所用连接池时如果实现类不存在调用此方法会自动抛出异常从而切换到下一种连接池的检测
* @param setting 数据库连接配置如果为{@code null}则读取全局自定义或默认配置
*/
public AbstractDSFactory(final String dataSourceName, final Class<? extends DataSource> dataSourceClass, Setting setting) {
//此参数的作用是在detectDSFactory方法自动检测所用连接池时如果实现类不存在调用此方法会自动抛出异常从而切换到下一种连接池的检测
Assert.notNull(dataSourceClass);
this.dataSourceName = dataSourceName;
if (null == setting) {
setting = GlobalDbConfig.createDbSetting();
}
// 读取配置用于SQL打印
DbUtil.setShowSqlGlobal(setting);
this.setting = setting;
this.dsMap = new SafeConcurrentHashMap<>();
}
/**
* 获取配置用于自定义添加配置项
*
* @return Setting
* @since 4.0.3
*/
public Setting getSetting() {
return this.setting;
}
@Override
public String getDataSourceName() {
return this.dataSourceName;
}
@Override
public DataSource getDataSource(String group) {
if (group == null) {
group = StrUtil.EMPTY;
}
// 如果已经存在已有数据源连接池直接返回
return dsMap.computeIfAbsent(group, this::_createDataSource);
}
@Override
synchronized public void closeDataSource(String group) {
if (group == null) {
group = StrUtil.EMPTY;
}
final DSWrapper ds = dsMap.get(group);
if (ds != null) {
ds.close();
dsMap.remove(group);
}
}
@Override
public void close() {
if (MapUtil.isNotEmpty(dsMap)) {
final Collection<DSWrapper> values = dsMap.values();
for (final DSWrapper ds : values) {
ds.close();
}
dsMap.clear();
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dataSourceName == null) ? 0 : dataSourceName.hashCode());
result = prime * result + ((setting == null) ? 0 : setting.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final AbstractDSFactory other = (AbstractDSFactory) obj;
if (dataSourceName == null) {
if (other.dataSourceName != null) {
return false;
}
} else if (!dataSourceName.equals(other.dataSourceName)) {
return false;
}
if (setting == null) {
return other.setting == null;
} else {
return setting.equals(other.setting);
}
}
/**
* 创建新的{@link DataSource}<br>
* 子类通过实现此方法创建一个对接连接池的数据源
*
* @param jdbcUrl JDBC连接字符串
* @param driver 数据库驱动类名
* @param user 用户名
* @param pass 密码
* @param poolSetting 分组下的连接池配置文件
* @return {@link DataSource}
*/
protected abstract DataSource createDataSource(
String jdbcUrl, String driver, String user, String pass, Setting poolSetting);
/**
* 创建数据源对于不同连接池名称的的差异做兼容如用户配置user和username都表示用户名
*
* @param group 分组
* @return {@link DSWrapper} 数据源包装
*/
private DSWrapper _createDataSource(String group) {
if (group == null) {
group = StrUtil.EMPTY;
}
final Setting config = setting.getSetting(group);
if (MapUtil.isEmpty(config)) {
throw new DbRuntimeException("No config for group: [{}]", group);
}
// 基本信息
final String url = config.getAndRemove(DSKeys.KEY_ALIAS_URL);
if (StrUtil.isBlank(url)) {
throw new DbRuntimeException("No JDBC URL for group: [{}]", group);
}
// 移除用户可能误加入的show sql配置项
// issue#I3VW0R@Gitee
removeShowSqlParams(config);
// 自动识别Driver
String driver = config.getAndRemove(DSKeys.KEY_ALIAS_DRIVER);
if (StrUtil.isBlank(driver)) {
driver = DriverUtil.identifyDriver(url);
}
final String user = config.getAndRemove(DSKeys.KEY_ALIAS_USER);
final String pass = config.getAndRemove(DSKeys.KEY_ALIAS_PASSWORD);
return DSWrapper.wrap(createDataSource(url, driver, user, pass, config), driver);
}
/**
* 移除配置文件中的Show SQL相关配置项<br>
* 此方法用于移除用户配置在分组下的配置项目
*
* @param setting 配置项
* @since 5.7.2
*/
private static void removeShowSqlParams(final Setting setting) {
setting.remove(DSKeys.KEY_SHOW_SQL);
setting.remove(DSKeys.KEY_FORMAT_SQL);
setting.remove(DSKeys.KEY_SHOW_PARAMS);
setting.remove(DSKeys.KEY_SQL_LEVEL);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Copyright (c) 2023. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
@ -12,17 +12,12 @@
package org.dromara.hutool.db.ds;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.setting.Setting;
import javax.sql.DataSource;
import java.io.Closeable;
import java.io.Serializable;
/**
* 多数据源工厂方法接口借助不同配置同一个工厂可以连接多个相同或不同的数据库但是连接池只能使用一种<br>
* 通过实现{@link #getDataSource(String)} 方法完成数据源的获取<br>
* 如果{@link DataSource} 的实现是数据库连接池库应该在getDataSource调用时创建数据源并缓存关系如下
* 多数据源{@link DataSource}工厂方法接口借助不同配置同一个工厂可以连接多个相同或不同的数据库但是连接池只能使用一种<br>
* 通过实现{@link #createDataSource(DbConfig)} 方法完成数据源的创建关系如下<br>
* <pre>
* DSFactory
* _____________________|____________________
@ -33,13 +28,9 @@ import java.io.Serializable;
* MySQL SQLite SQLServer XXXDB XXXDB2
* </pre>
*
* <p>
* 工厂创建请使用{@link DSUtil#createFactory(Setting)}
* </p>
*
* @author Looly
*/
public interface DSFactory extends Closeable, Serializable {
public interface DSFactory extends Serializable {
/**
* 获取自定义的数据源名称用于识别连接池
@ -49,33 +40,10 @@ public interface DSFactory extends Closeable, Serializable {
String getDataSourceName();
/**
* 获得默认数据源""分组的数据源
* 创建数据源
*
* @return 数据源
* @param config 数据库配置
* @return {@link DataSource}
*/
default DataSource getDataSource() {
return getDataSource(StrUtil.EMPTY);
}
/**
* 获得分组对应数据源
*
* @param group 分组名
* @return 数据源
*/
DataSource getDataSource(String group);
/**
* 关闭默认数据源空组
*/
default void closeDataSource() {
closeDataSource(StrUtil.EMPTY);
}
/**
* 关闭(归还)对应数据源
*
* @param group 分组
*/
void closeDataSource(String group);
DataSource createDataSource(DbConfig config);
}

View File

@ -0,0 +1,265 @@
/*
* Copyright (c) 2023. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db.ds;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.map.SafeConcurrentHashMap;
import org.dromara.hutool.core.spi.SpiUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.GlobalDbConfig;
import org.dromara.hutool.db.driver.DriverUtil;
import org.dromara.hutool.log.LogUtil;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.setting.props.Props;
import javax.sql.DataSource;
import java.io.Closeable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* 数据源池用于支持多数据源<br>
* 在指定Setting中配置多个数据源时通过分组group区分<br>
* 每次获得一个数据源则缓存在pool中确保数据源保持单例状态
*
* @author Looly
* @since 6.0.0
*/
public class DSPool implements Closeable {
private static final String CONNECTION_PREFIX = "connection.";
private static class SingletonHolder {
private static final DSPool INSTANCE = new DSPool();
}
/**
* 获取单例池对象
*
* @return 数据源池
*/
public static DSPool getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 数据库连接配置文件
*/
private final Setting setting;
/**
* 数据源池
*/
private final Map<String, DSWrapper> pool;
/**
* 连接工厂
*/
private DSFactory factory;
/**
* 构造通过SPI方式自动获取用户引入的连接池使用classpath:db.setting
*/
public DSPool() {
this(null);
}
/**
* 构造通过SPI方式自动获取用户引入的连接池
*
* @param setting 数据库配置支持多数据源{@code null}表示读取classpath:db.setting
*/
public DSPool(final Setting setting) {
this(setting, null);
}
/**
* 构造
*
* @param setting 数据库配置支持多数据源{@code null}表示读取classpath:db.setting
* @param factory 数据源工厂用于创建数据源{@code null}表示使用SPI自动获取
*/
public DSPool(final Setting setting, final DSFactory factory) {
this.setting = null != setting ? setting : GlobalDbConfig.createDbSetting();
this.factory = null != factory ? factory : SpiUtil.loadFirstAvailable(DSFactory.class);
this.pool = new SafeConcurrentHashMap<>();
}
/**
* 获取配置用于自定义添加配置项
*
* @return Setting
* @since 4.0.3
*/
public Setting getSetting() {
return this.setting;
}
/**
* 获取数据源名称用于识别当前使用连接池类型
*
* @return 数据源名称
*/
public String getDataSourceName() {
return this.factory.getDataSourceName();
}
/**
* 设置自定义的{@link DSFactory}
*
* @param factory {@link DSFactory}
* @return this
*/
public DSPool setFactory(final DSFactory factory) {
this.factory = factory;
LogUtil.debug("Custom use [{}] DataSource.", factory.getDataSourceName());
return this;
}
/**
* 获取指定分组的数据源单例获取
*
* @param group 分组{@code null}表示默认分组
* @return 数据源
*/
public DataSource getDataSource(String group) {
if (group == null) {
group = StrUtil.EMPTY;
}
// 如果已经存在已有数据源连接池直接返回
return pool.computeIfAbsent(group, this::createDSWrapper);
}
/**
* 关闭指定数据源
*
* @param group 分组
* @return this
*/
public DSPool closeDataSource(String group) {
if (group == null) {
group = StrUtil.EMPTY;
}
// 此处线程安全任意线程进入一旦remove完成后续线程调用remove后都为null
final DSWrapper removed = pool.remove(group);
if (null != removed) {
IoUtil.closeQuietly(removed);
}
return this;
}
@Override
public void close() {
final Map<String, DSWrapper> pool = this.pool;
if (MapUtil.isNotEmpty(pool)) {
// 此处线程安全多线程调用可能多次调用clear不影响
final Collection<DSWrapper> values = pool.values();
pool.clear();
for (final DSWrapper ds : values) {
ds.close();
}
}
}
/**
* 创建数据源对于不同连接池名称的的差异做兼容如用户配置user和username都表示用户名
*
* @param group 分组{@code null}表示默认分组
* @return {@link DSWrapper} 数据源包装
*/
private DSWrapper createDSWrapper(String group) {
if (group == null) {
group = StrUtil.EMPTY;
}
final Setting subSetting = setting.getSetting(group);
if (MapUtil.isEmpty(subSetting)) {
throw new DbRuntimeException("No config for group: [{}]", group);
}
final DbConfig dbConfig = toDbConfig(subSetting);
return DSWrapper.wrap(factory.createDataSource(dbConfig), dbConfig.getDriver());
}
/**
* {@link Setting}数据库配置 {@link DbConfig}
*
* @param setting {@link Setting}数据库配置
* @return {@link DbConfig}
*/
private static DbConfig toDbConfig(final Setting setting) {
// 基本信息
final String url = setting.getAndRemove(DSKeys.KEY_ALIAS_URL);
if (StrUtil.isBlank(url)) {
throw new DbRuntimeException("No JDBC URL!");
}
// 移除用户可能误加入的show sql配置项
// issue#I3VW0R@Gitee
removeShowSqlParams(setting);
// 自动识别Driver
String driver = setting.getAndRemove(DSKeys.KEY_ALIAS_DRIVER);
if (StrUtil.isBlank(driver)) {
driver = DriverUtil.identifyDriver(url);
}
final DbConfig dbConfig = DbConfig.of()
.setUrl(url)
.setDriver(driver)
.setUser(setting.getAndRemove(DSKeys.KEY_ALIAS_USER))
.setPass(setting.getAndRemove(DSKeys.KEY_ALIAS_PASSWORD));
// remarks等连接配置since 5.3.8
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = setting.getAndRemove(key);
if (StrUtil.isNotBlank(connValue)) {
dbConfig.addConnProps(key, connValue);
}
}
// 自定义连接属性
final Props connProps = new Props();
final Set<String> keys = setting.keySet();
for (final String key : keys) {
if (key.startsWith(CONNECTION_PREFIX)) {
connProps.set(StrUtil.subSuf(key, CONNECTION_PREFIX.length()), setting.remove(key));
}
}
dbConfig.setConnProps(connProps);
// 池属性
dbConfig.setPoolProps(setting.toProps());
return dbConfig;
}
/**
* 移除配置文件中的Show SQL相关配置项<br>
* 此方法用于移除用户配置在分组下的配置项目
*
* @param setting 配置项
* @since 5.7.2
*/
private static void removeShowSqlParams(final Setting setting) {
setting.remove(DSKeys.KEY_SHOW_SQL);
setting.remove(DSKeys.KEY_FORMAT_SQL);
setting.remove(DSKeys.KEY_SHOW_PARAMS);
setting.remove(DSKeys.KEY_SQL_LEVEL);
}
}

View File

@ -12,19 +12,15 @@
package org.dromara.hutool.db.ds;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.spi.ListServiceLoader;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.GlobalDbConfig;
import org.dromara.hutool.log.LogUtil;
import org.dromara.hutool.setting.Setting;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/**
* {@link DataSource}{@link DSFactory}相关工具类<br>
* {@link DataSource}相关工具类<br>
* 主要提供数据源工厂的创建和数据源的获取
*
* @author looly
@ -78,7 +74,7 @@ public class DSUtil {
* @return 数据源
*/
public static DataSource getDS(final String group) {
return GlobalDSFactory.get().getDataSource(group);
return DSPool.getInstance().getDataSource(group);
}
/**
@ -95,47 +91,7 @@ public class DSUtil {
* @return 自定义的数据源工厂
*/
public static DSFactory setGlobalDSFactory(final DSFactory dsFactory) {
return GlobalDSFactory.set(dsFactory);
}
/**
* 创建数据源实现工厂<br>
* 此方法通过试错方式查找引入项目的连接池库按照优先级寻找一旦寻找到则创建对应的数据源工厂<br>
* 连接池优先级Hikari &gt; Druid &gt; Tomcat &gt; Dbcp &gt; C3p0 &gt; Hutool Pooled
*
* @param setting 数据库配置项
* @return 日志实现类
*/
public static DSFactory createFactory(final Setting setting) {
final DSFactory dsFactory = _createFactory(setting);
LogUtil.debug("Use [{}] DataSource As Default.", dsFactory.getDataSourceName());
DSPool.getInstance().setFactory(dsFactory);
return dsFactory;
}
/**
* 创建数据源实现工厂<br>
* 此方法通过试错方式查找引入项目的连接池库按照优先级寻找一旦寻找到则创建对应的数据源工厂<br>
* 连接池优先级Hikari &gt; Druid &gt; Tomcat &gt; BeeCP &gt; Dbcp &gt; C3p0 &gt; Hutool Pooled<br>
* META-INF/services/org.dromara.hutool.db.ds.DSFactory
*
* @param setting 数据库配置项
* @return 日志实现类
* @since 4.1.3
*/
private static DSFactory _createFactory(Setting setting) {
if (null == setting) {
setting = GlobalDbConfig.createDbSetting();
}
final ListServiceLoader<DSFactory> loader = ListServiceLoader.of(DSFactory.class);
final int size = loader.size();
for (int i = 0; i < size; i++) {
try {
return ConstructorUtil.newInstance(loader.getServiceClass(i), setting);
} catch (final NoClassDefFoundError | NoSuchMethodError e) {
// ignore
}
}
throw new DbRuntimeException("No DSFactory implement available!");
}
}

View File

@ -0,0 +1,238 @@
/*
* Copyright (c) 2023. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db.ds;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.driver.DriverUtil;
import java.util.Properties;
/**
* 数据库配置包括
* <ul>
* <li>基本配置项如driverurluserpassword等</li>
* <li>连接配置如remarksuseInformationSchema等</li>
* <li>连接池配置如初始容量最大容量等取决于连接池库具体要求</li>
* </ul>
*
* @author Looly
*/
public class DbConfig {
/**
* 创建DbConfig
*
* @return DbConfig
*/
public static DbConfig of() {
return new DbConfig();
}
private String driver; //数据库驱动
private String url; //jdbc url
private String user; //用户名
private String pass; //密码
// 连接配置
private Properties connProps;
// 连接池配置
private Properties poolProps;
/**
* 构造
*/
public DbConfig() {
}
/**
* 构造
*
* @param url jdbc url
* @param user 用户名
* @param pass 密码
*/
public DbConfig(final String url, final String user, final String pass) {
init(url, user, pass);
}
/**
* 初始化
*
* @param url jdbc url
* @param user 用户名
* @param pass 密码
*/
public void init(final String url, final String user, final String pass) {
this.url = url;
this.user = user;
this.pass = pass;
this.driver = DriverUtil.identifyDriver(url);
try {
Class.forName(this.driver);
} catch (final ClassNotFoundException e) {
throw new DbRuntimeException(e, "Get jdbc driver from [{}] error!", url);
}
}
/**
* 获取JDBC驱动
*
* @return JDBC驱动
*/
public String getDriver() {
return driver;
}
/**
* 设置JDBC驱动
*
* @param driver JDBC驱动
* @return this
*/
public DbConfig setDriver(final String driver) {
this.driver = driver;
return this;
}
/**
* 获取JDBC URL
*
* @return JDBC URL
*/
public String getUrl() {
return url;
}
/**
* 设置JDBC URL
*
* @param url JDBC URL
* @return this
*/
public DbConfig setUrl(final String url) {
this.url = url;
return this;
}
/**
* 获取用户名
*
* @return 用户名
*/
public String getUser() {
return user;
}
/**
* 设置用户名
*
* @param user 用户名
* @return this
*/
public DbConfig setUser(final String user) {
this.user = user;
return this;
}
/**
* 获取密码
*
* @return 密码
*/
public String getPass() {
return pass;
}
/**
* 设置密码
*
* @param pass 密码
* @return this
*/
public DbConfig setPass(final String pass) {
this.pass = pass;
return this;
}
/**
* 获取连接属性
*
* @return 连接属性
*/
public Properties getConnProps() {
return connProps;
}
/**
* 设置连接属性
*
* @param connProps 连接属性
* @return this
*/
public DbConfig setConnProps(final Properties connProps) {
this.connProps = connProps;
return this;
}
/**
* 增加连接属性
*
* @param key 属性名
* @param value 属性值
* @return this
*/
public DbConfig addConnProps(final String key, final String value) {
if (null == this.connProps) {
this.connProps = new Properties();
}
this.connProps.setProperty(key, value);
return this;
}
/**
* 获取连接池属性
*
* @return 连接池属性
*/
public Properties getPoolProps() {
return poolProps;
}
/**
* 设置连接池属性
*
* @param poolProps 连接池属性
* @return this
*/
public DbConfig setPoolProps(final Properties poolProps) {
this.poolProps = poolProps;
return this;
}
/**
* 增加连接池属性
*
* @param key 属性名
* @param value 属性值
* @return this
*/
public DbConfig addPoolProps(final String key, final String value) {
if (null == this.poolProps) {
this.poolProps = new Properties();
}
this.poolProps.setProperty(key, value);
return this;
}
}

View File

@ -1,91 +0,0 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db.ds;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.util.RuntimeUtil;
import org.dromara.hutool.log.LogUtil;
/**
* 全局单例数据源工厂<br>
* 一般情况下一个应用默认只使用一种数据库连接池因此维护一个全局的数据源工厂类减少判断连接池类型造成的性能浪费
*
* @author looly
* @since 4.0.2
*/
public class GlobalDSFactory {
private static volatile DSFactory factory;
private static final Object lock = new Object();
/*
* 设置在JVM关闭时关闭所有数据库连接
*/
static {
// JVM关闭时关闭所有连接池
RuntimeUtil.addShutdownHook(()->{
if (null != factory) {
IoUtil.closeQuietly(factory);
LogUtil.debug("DataSource: [{}] closed.", factory.getDataSourceName());
factory = null;
}
});
}
/**
* 获取默认的数据源工厂读取默认数据库配置文件<br>
* 此处使用懒加载模式在第一次调用此方法时才创建默认数据源工厂<br>
* 如果想自定义全局的数据源工厂请在第一次调用此方法前调用{@link #set(DSFactory)} 方法自行定义
*
* @return 当前使用的数据源工厂
*/
public static DSFactory get() {
if (null == factory) {
synchronized (lock) {
if (null == factory) {
factory = DSUtil.createFactory(null);
}
}
}
return factory;
}
/**
* 设置全局的数据源工厂<br>
* 在项目中存在多个连接池库的情况下我们希望使用低优先级的库时使用此方法自定义之<br>
* 重新定义全局的数据源工厂此方法可在以下两种情况下调用
*
* <pre>
* 1. 在get方法调用前调用此方法来自定义全局的数据源工厂
* 2. 替换已存在的全局数据源工厂当已存在时会自动关闭
* </pre>
*
* @param customDSFactory 自定义数据源工厂
* @return 自定义的数据源工厂
*/
public static DSFactory set(final DSFactory customDSFactory) {
synchronized (lock) {
if (null != factory) {
if (factory.equals(customDSFactory)) {
return factory;// 数据源工厂不变时返回原数据源工厂
}
// 自定义数据源工厂前关闭之前的数据源
IoUtil.closeQuietly(factory);
}
LogUtil.debug("Custom use [{}] DataSource.", customDSFactory.getDataSourceName());
factory = customDSFactory;
}
return factory;
}
}

View File

@ -14,55 +14,39 @@ package org.dromara.hutool.db.ds.bee;
import cn.beecp.BeeDataSource;
import cn.beecp.BeeDataSourceConfig;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
import javax.sql.DataSource;
import java.util.Properties;
/**
* BeeCP数据源工厂类
*
* @author Looly
*/
public class BeeDSFactory extends AbstractDSFactory {
public class BeeDSFactory implements DSFactory {
private static final long serialVersionUID = 1L;
/**
* 连接池名称BeeCP
*/
public static final String DS_NAME = "BeeCP";
/**
* 构造使用默认配置文件
*/
public BeeDSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置文件
*/
public BeeDSFactory(final Setting setting) {
super(DS_NAME, BeeDataSource.class, setting);
@Override
public String getDataSourceName() {
return "BeeCP";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
public DataSource createDataSource(final DbConfig config) {
final BeeDataSourceConfig beeConfig = new BeeDataSourceConfig(
config.getDriver(), config.getUrl(), config.getUser(), config.getPass());
final BeeDataSourceConfig beeConfig = new BeeDataSourceConfig(driver, jdbcUrl, user, pass);
poolSetting.toBean(beeConfig);
// 连接池和其它选项
Props.of(config.getPoolProps()).toBean(beeConfig);
// remarks等特殊配置since 5.3.8
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = poolSetting.getAndRemove(key);
if (StrUtil.isNotBlank(connValue)) {
beeConfig.addConnectProperty(key, connValue);
}
// 连接配置
final Properties connProps = config.getConnProps();
if(MapUtil.isNotEmpty(connProps)){
connProps.forEach((key, value)->beeConfig.addConnectProperty(key.toString(), value));
}
return new BeeDataSource(beeConfig);

View File

@ -12,17 +12,16 @@
package org.dromara.hutool.db.ds.c3p0;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.setting.props.Props;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.util.Properties;
/**
* C3P0数据源工厂类
@ -30,59 +29,36 @@ import java.beans.PropertyVetoException;
* @author Looly
*
*/
public class C3p0DSFactory extends AbstractDSFactory {
public class C3p0DSFactory implements DSFactory {
private static final long serialVersionUID = -6090788225842047281L;
/**
* 数据源名称C3P0
*/
public static final String DS_NAME = "C3P0";
/**
* 构造使用默认配置
*/
public C3p0DSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置
*/
public C3p0DSFactory(final Setting setting) {
super(DS_NAME, ComboPooledDataSource.class, setting);
@Override
public String getDataSourceName() {
return "C3P0";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
public DataSource createDataSource(final DbConfig config) {
final ComboPooledDataSource ds = new ComboPooledDataSource();
// remarks等特殊配置since 5.3.8
final Props connProps = new Props();
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = poolSetting.getAndRemove(key);
if(StrUtil.isNotBlank(connValue)){
connProps.setProperty(key, connValue);
}
ds.setJdbcUrl(config.getUrl());
try {
ds.setDriverClass(config.getDriver());
} catch (final PropertyVetoException e) {
throw new DbRuntimeException(e);
}
ds.setUser(config.getUser());
ds.setPassword(config.getPass());
// 连接池和其它选项
Props.of(config.getPoolProps()).toBean(ds);
// 连接配置
final Properties connProps = config.getConnProps();
if(MapUtil.isNotEmpty(connProps)){
ds.setProperties(connProps);
}
ds.setJdbcUrl(jdbcUrl);
try {
ds.setDriverClass(driver);
} catch (final PropertyVetoException e) {
throw new DbRuntimeException(e);
}
ds.setUser(user);
ds.setPassword(pass);
// 注入属性
poolSetting.toBean(ds);
return ds;
}
}

View File

@ -12,13 +12,14 @@
package org.dromara.hutool.db.ds.dbcp;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
import org.apache.commons.dbcp2.BasicDataSource;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
import javax.sql.DataSource;
import java.util.Properties;
/**
* DBCP2数据源工厂类
@ -26,51 +27,32 @@ import javax.sql.DataSource;
* @author Looly
*
*/
public class DbcpDSFactory extends AbstractDSFactory {
private static final long serialVersionUID = -9133501414334104548L;
public class DbcpDSFactory implements DSFactory {
private static final long serialVersionUID = 1L;
/**
* 数据源名称commons-dbcp2
*/
public static final String DS_NAME = "commons-dbcp2";
/**
* 构造使用默认配置文件
*/
public DbcpDSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置
*/
public DbcpDSFactory(final Setting setting) {
super(DS_NAME, BasicDataSource.class, setting);
@Override
public String getDataSourceName() {
return "commons-dbcp2";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
public DataSource createDataSource(final DbConfig config) {
final BasicDataSource ds = new BasicDataSource();
ds.setUrl(jdbcUrl);
ds.setDriverClassName(driver);
ds.setUsername(user);
ds.setPassword(pass);
ds.setUrl(config.getUrl());
ds.setDriverClassName(config.getDriver());
ds.setUsername(config.getUser());
ds.setPassword(config.getPass());
// remarks等特殊配置since 5.3.8
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = poolSetting.getAndRemove(key);
if(StrUtil.isNotBlank(connValue)){
ds.addConnectionProperty(key, connValue);
}
// 连接池和其它选项
Props.of(config.getPoolProps()).toBean(ds);
// 连接配置
final Properties connProps = config.getConnProps();
if(MapUtil.isNotEmpty(connProps)){
connProps.forEach((key, value)->ds.addConnectionProperty(key.toString(), value.toString()));
}
// 注入属性
poolSetting.toBean(ds);
return ds;
}
}

View File

@ -12,14 +12,15 @@
package org.dromara.hutool.db.ds.druid;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.setting.props.Props;
import com.alibaba.druid.pool.DruidDataSource;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
import javax.sql.DataSource;
import java.util.Properties;
/**
* Druid数据源工厂类
@ -27,53 +28,33 @@ import javax.sql.DataSource;
* @author Looly
*
*/
public class DruidDSFactory extends AbstractDSFactory {
public class DruidDSFactory implements DSFactory {
private static final long serialVersionUID = 4680621702534433222L;
/**
* 数据源名称Druid
*/
public static final String DS_NAME = "Druid";
/**
* 构造使用默认配置文件
*/
public DruidDSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置
*/
public DruidDSFactory(final Setting setting) {
super(DS_NAME, DruidDataSource.class, setting);
@Override
public String getDataSourceName() {
return "Druid";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
public DataSource createDataSource(final DbConfig config) {
final DruidDataSource ds = new DruidDataSource();
// 基本信息
ds.setUrl(jdbcUrl);
ds.setDriverClassName(driver);
ds.setUsername(user);
ds.setPassword(pass);
ds.setUrl(config.getUrl());
ds.setDriverClassName(config.getDriver());
ds.setUsername(config.getUser());
ds.setPassword(config.getPass());
// remarks等特殊配置since 5.3.8
// Druid中也可以通过 druid.connectProperties 属性设置
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = poolSetting.getAndRemove(key);
if(StrUtil.isNotBlank(connValue)){
ds.addConnectionProperty(key, connValue);
}
// 连接配置
final Properties connProps = config.getConnProps();
if(MapUtil.isNotEmpty(connProps)){
connProps.forEach((key, value)->ds.addConnectionProperty(key.toString(), value.toString()));
}
// Druid连接池配置信息规范化属性名
final Props druidProps = new Props();
poolSetting.forEach((key, value)-> druidProps.put(StrUtil.addPrefixIfNot(key, "druid."), value));
config.getPoolProps().forEach((key, value)-> druidProps.set(StrUtil.addPrefixIfNot(key.toString(), "druid."), value));
ds.configFromPropeties(druidProps);
//issue#I4ZKCW 某些非属性设置单独设置

View File

@ -12,13 +12,11 @@
package org.dromara.hutool.db.ds.hikari;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.setting.props.Props;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
import javax.sql.DataSource;
@ -28,59 +26,41 @@ import javax.sql.DataSource;
* @author Looly
*
*/
public class HikariDSFactory extends AbstractDSFactory {
public class HikariDSFactory implements DSFactory {
private static final long serialVersionUID = -8834744983614749401L;
/**
* 数据源名称HikariCP
*/
public static final String DS_NAME = "HikariCP";
/**
* 构造使用默认配置文件
*/
public HikariDSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置
*/
public HikariDSFactory(final Setting setting) {
super(DS_NAME, HikariDataSource.class, setting);
@Override
public String getDataSourceName() {
return "HikariCP";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
// remarks等特殊配置since 5.3.8
final Props connProps = new Props();
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = poolSetting.getAndRemove(key);
if(StrUtil.isNotBlank(connValue)){
connProps.setProperty(key, connValue);
}
}
public DataSource createDataSource(final DbConfig config) {
final Props props = new Props();
final Props config = new Props();
config.putAll(poolSetting);
config.put("jdbcUrl", jdbcUrl);
// 基本信息
props.put("jdbcUrl", config.getUrl());
final String driver = config.getDriver();
if (null != driver) {
config.put("driverClassName", driver);
props.put("driverClassName", driver);
}
final String user = config.getUser();
if (null != user) {
config.put("username", user);
props.put("username", user);
}
final String pass = config.getPass();
if (null != pass) {
config.put("password", pass);
props.put("password", pass);
}
final HikariConfig hikariConfig = new HikariConfig(config);
hikariConfig.setDataSourceProperties(connProps);
// 连接池信息
props.putAll(config.getPoolProps());
final HikariConfig hikariConfig = new HikariConfig(props);
// 连接信息
hikariConfig.setDataSourceProperties(config.getConnProps());
return new HikariDataSource(hikariConfig);
}
}

View File

@ -14,9 +14,9 @@ package org.dromara.hutool.db.ds.jndi;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DSUtil;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.db.ds.DbConfig;
import javax.sql.DataSource;
@ -31,33 +31,17 @@ import javax.sql.DataSource;
* @author Looly
*
*/
public class JndiDSFactory extends AbstractDSFactory {
public class JndiDSFactory implements DSFactory {
private static final long serialVersionUID = 1573625812927370432L;
/**
* 数据源名称JNDI DataSource
*/
public static final String DS_NAME = "JNDI DataSource";
/**
* 构造使用默认配置文件
*/
public JndiDSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置
*/
public JndiDSFactory(final Setting setting) {
super(DS_NAME, null, setting);
@Override
public String getDataSourceName() {
return "JNDI DataSource";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
final String jndiName = poolSetting.getStr("jndi");
public DataSource createDataSource(final DbConfig config) {
final String jndiName = config.getPoolProps().getProperty("jndi");
if (StrUtil.isEmpty(jndiName)) {
throw new DbRuntimeException("No setting name [jndi] for this group.");
}

View File

@ -15,6 +15,7 @@ package org.dromara.hutool.db.ds.pooled;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.pool.Poolable;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
import java.sql.Connection;
@ -40,7 +41,7 @@ public class PooledConnection extends ConnectionWrapper implements Poolable<Conn
* @param config 数据库配置
* @param dataSource 数据源
*/
public PooledConnection(final PooledDbConfig config, final PooledDataSource dataSource) {
public PooledConnection(final DbConfig config, final PooledDataSource dataSource) {
final Props info = new Props();
final String user = config.getUser();
if (user != null) {

View File

@ -12,10 +12,8 @@
package org.dromara.hutool.db.ds.pooled;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import javax.sql.DataSource;
@ -25,53 +23,16 @@ import javax.sql.DataSource;
* @author Looly
*
*/
public class PooledDSFactory extends AbstractDSFactory {
public class PooledDSFactory implements DSFactory {
private static final long serialVersionUID = 8093886210895248277L;
/**
* 数据源名称Hutool-Pooled-DataSource
*/
public static final String DS_NAME = "Hutool-Pooled-DataSource";
/**
* 构造使用默认配置文件
*/
public PooledDSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置
*/
public PooledDSFactory(final Setting setting) {
super(DS_NAME, PooledDataSource.class, setting);
@Override
public String getDataSourceName() {
return "Hutool-Pooled-DataSource";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
final PooledDbConfig pooledDbConfig = new PooledDbConfig();
pooledDbConfig.setUrl(jdbcUrl);
pooledDbConfig.setDriver(driver);
pooledDbConfig.setUser(user);
pooledDbConfig.setPass(pass);
// 连接池相关信息
pooledDbConfig.setInitialSize(poolSetting.getInt("initialSize", 0));
pooledDbConfig.setMinIdle(poolSetting.getInt("minIdle", 0));
pooledDbConfig.setMaxActive(poolSetting.getInt("maxActive", 8));
pooledDbConfig.setMaxWait(poolSetting.getLong("maxWait", 6000L));
// remarks等特殊配置since 5.3.8
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = poolSetting.get(key);
if(StrUtil.isNotBlank(connValue)){
pooledDbConfig.addConnProps(key, connValue);
}
}
return new PooledDataSource(pooledDbConfig);
public DataSource createDataSource(final DbConfig config) {
return new PooledDataSource(config);
}
}

View File

@ -18,7 +18,9 @@ import org.dromara.hutool.core.pool.ObjectPool;
import org.dromara.hutool.core.pool.partition.PartitionObjectPool;
import org.dromara.hutool.core.pool.partition.PartitionPoolConfig;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.db.ds.simple.AbstractDataSource;
import org.dromara.hutool.setting.props.Props;
import java.sql.Connection;
import java.sql.SQLException;
@ -31,6 +33,11 @@ import java.sql.SQLException;
*/
public class PooledDataSource extends AbstractDataSource {
private static final String KEY_MAX_WAIT = "maxWait";
private static final String KEY_INITIAL_SIZE = "initialSize";
private static final String KEY_MAX_ACTIVE = "maxActive";
private final int maxWait;
private final ObjectPool<Connection> connPool;
/**
@ -38,12 +45,16 @@ public class PooledDataSource extends AbstractDataSource {
*
* @param config 数据库池配置
*/
public PooledDataSource(final PooledDbConfig config) {
public PooledDataSource(final DbConfig config) {
final Props poolProps = Props.of(config.getPoolProps());
this.maxWait = poolProps.getInt(KEY_MAX_WAIT, 6000);
final PartitionPoolConfig poolConfig = (PartitionPoolConfig) PartitionPoolConfig.of()
.setPartitionSize(1)
.setMaxWait(config.getMaxWait())
.setMinSize(config.getInitialSize())
.setMaxSize(config.getMaxActive());
.setMaxWait(this.maxWait)
.setMinSize(poolProps.getInt(KEY_INITIAL_SIZE, 0))
.setMaxSize(poolProps.getInt(KEY_MAX_ACTIVE, 8));
this.connPool = new PartitionObjectPool<>(poolConfig, createConnFactory(config));
}
@ -78,7 +89,7 @@ public class PooledDataSource extends AbstractDataSource {
* @param config 数据库配置
* @return {@link ObjectFactory}
*/
private ObjectFactory<Connection> createConnFactory(final PooledDbConfig config) {
private ObjectFactory<Connection> createConnFactory(final DbConfig config) {
return new ObjectFactory<Connection>() {
@Override
public Connection create() {
@ -88,7 +99,8 @@ public class PooledDataSource extends AbstractDataSource {
@Override
public boolean validate(final Connection connection) {
try {
return null != connection && connection.isValid((int) config.getMaxWait());
return null != connection
&& connection.isValid(maxWait);
} catch (final SQLException e) {
throw new DbRuntimeException(e);
}

View File

@ -1,117 +0,0 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db.ds.pooled;
import org.dromara.hutool.db.ds.simple.DbConfig;
/**
* 数据库配置
*
* @author Looly
*/
public class PooledDbConfig extends DbConfig {
private int initialSize; //初始连接数
private int minIdle; //最小闲置连接数
private int maxActive; //最大活跃连接数
private long maxWait; //连接的超时等待
/**
* 构造
*/
public PooledDbConfig() {
}
/**
* 构造
*
* @param url jdbc url
* @param user 用户名
* @param pass 密码
*/
public PooledDbConfig(final String url, final String user, final String pass) {
super(url, user, pass);
}
/**
* 获取初始大小
*
* @return 初始大小
*/
public int getInitialSize() {
return initialSize;
}
/**
* 设置初始大小
*
* @param initialSize 初始大小
*/
public void setInitialSize(final int initialSize) {
this.initialSize = initialSize;
}
/**
* 获取最小闲置连接数
*
* @return 最小闲置连接数
*/
public int getMinIdle() {
return minIdle;
}
/**
* 设置最小闲置连接数
*
* @param minIdle 最小闲置连接数
*/
public void setMinIdle(final int minIdle) {
this.minIdle = minIdle;
}
/**
* 获取最大活跃连接数
*
* @return 最大活跃连接数
*/
public int getMaxActive() {
return maxActive;
}
/**
* 设置最大活跃连接数
*
* @param maxActive 最大活跃连接数
*/
public void setMaxActive(final int maxActive) {
this.maxActive = maxActive;
}
/**
* 获取连接的超时等待
*
* @return 连接的超时等待
*/
public long getMaxWait() {
return maxWait;
}
/**
* 设置连接的超时等待
*
* @param maxWait 连接的超时等待
*/
public void setMaxWait(final long maxWait) {
this.maxWait = maxWait;
}
}

View File

@ -1,117 +0,0 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db.ds.simple;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.driver.DriverUtil;
import java.util.Properties;
/**
* 数据库配置
*
* @author Looly
*/
public class DbConfig {
private String driver; //数据库驱动
private String url; //jdbc url
private String user; //用户名
private String pass; //密码
// 连接配置
private Properties connProps;
/**
* 构造
*/
public DbConfig() {
}
/**
* 构造
*
* @param url jdbc url
* @param user 用户名
* @param pass 密码
*/
public DbConfig(final String url, final String user, final String pass) {
init(url, user, pass);
}
/**
* 初始化
*
* @param url jdbc url
* @param user 用户名
* @param pass 密码
*/
public void init(final String url, final String user, final String pass) {
this.url = url;
this.user = user;
this.pass = pass;
this.driver = DriverUtil.identifyDriver(url);
try {
Class.forName(this.driver);
} catch (final ClassNotFoundException e) {
throw new DbRuntimeException(e, "Get jdbc driver from [{}] error!", url);
}
}
public String getDriver() {
return driver;
}
public void setDriver(final String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(final String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(final String user) {
this.user = user;
}
public String getPass() {
return pass;
}
public void setPass(final String pass) {
this.pass = pass;
}
public Properties getConnProps() {
return connProps;
}
public void setConnProps(final Properties connProps) {
this.connProps = connProps;
}
public void addConnProps(final String key, final String value){
if(null == this.connProps){
this.connProps = new Properties();
}
this.connProps.setProperty(key, value);
}
}

View File

@ -12,8 +12,8 @@
package org.dromara.hutool.db.ds.simple;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import javax.sql.DataSource;
@ -23,36 +23,16 @@ import javax.sql.DataSource;
* @author Looly
*
*/
public class SimpleDSFactory extends AbstractDSFactory {
public class SimpleDSFactory implements DSFactory {
private static final long serialVersionUID = 4738029988261034743L;
/**
* 数据源名称Hutool-Simple-DataSource
*/
public static final String DS_NAME = "Hutool-Simple-DataSource";
/**
* 构造使用默认配置文件
*/
public SimpleDSFactory() {
this(null);
}
/**
* 构造使用自定义配置文件
*
* @param setting 配置
*/
public SimpleDSFactory(final Setting setting) {
super(DS_NAME, SimpleDataSource.class, setting);
@Override
public String getDataSourceName() {
return "Hutool-Simple-DataSource";
}
@Override
protected DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
final DbConfig dbConfig = new DbConfig(jdbcUrl, user, pass);
dbConfig.setDriver(driver);
dbConfig.setConnProps(poolSetting.getProps(Setting.DEFAULT_GROUP));
return new SimpleDataSource(dbConfig);
public DataSource createDataSource(final DbConfig config) {
return new SimpleDataSource(config);
}
}

View File

@ -13,6 +13,7 @@
package org.dromara.hutool.db.ds.simple;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
import java.sql.Connection;

View File

@ -12,65 +12,40 @@
package org.dromara.hutool.db.ds.tomcat;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.ds.AbstractDSFactory;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
import org.dromara.hutool.setting.props.Props;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.dromara.hutool.db.ds.DSFactory;
import org.dromara.hutool.db.ds.DbConfig;
import org.dromara.hutool.setting.props.Props;
/**
* Tomcat-Jdbc-Pool数据源工厂类
*
* @author Looly
*
*/
public class TomcatDSFactory extends AbstractDSFactory {
public class TomcatDSFactory implements DSFactory {
private static final long serialVersionUID = 4925514193275150156L;
/**
* 数据源名称Tomcat-Jdbc-Pool
*/
public static final String DS_NAME = "Tomcat-Jdbc-Pool";
/**
* 构造
*/
public TomcatDSFactory() {
this(null);
}
/**
* 构造自定义配置
*
* @param setting Setting数据库配置
*/
public TomcatDSFactory(final Setting setting) {
super(DS_NAME, DataSource.class, setting);
@Override
public String getDataSourceName() {
return "Tomcat-Jdbc-Pool";
}
@Override
protected javax.sql.DataSource createDataSource(final String jdbcUrl, final String driver, final String user, final String pass, final Setting poolSetting) {
public javax.sql.DataSource createDataSource(final DbConfig config) {
final PoolProperties poolProps = new PoolProperties();
poolProps.setUrl(jdbcUrl);
poolProps.setDriverClassName(driver);
poolProps.setUsername(user);
poolProps.setPassword(pass);
// remarks等特殊配置since 5.3.8
final Props connProps = new Props();
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = poolSetting.getAndRemove(key);
if(StrUtil.isNotBlank(connValue)){
connProps.setProperty(key, connValue);
}
}
poolProps.setDbProperties(connProps);
// 基本配置
poolProps.setUrl(config.getUrl());
poolProps.setDriverClassName(config.getDriver());
poolProps.setUsername(config.getUser());
poolProps.setPassword(config.getPass());
// 连接配置
poolProps.setDbProperties(config.getConnProps());
// 连接池相关参数
poolSetting.toBean(poolProps);
Props.of(config.getPoolProps()).toBean(poolProps);
return new DataSource(poolProps);
}

View File

@ -12,7 +12,6 @@
package org.dromara.hutool.db.ds;
import org.dromara.hutool.db.ds.simple.DbConfig;
import org.dromara.hutool.db.ds.simple.SimpleDataSource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

View File

@ -383,20 +383,20 @@ public class Setting extends AbsSetting implements Map<String, String> {
}
/**
* 转换为Properties对象原分组变为前缀
* 转换为{@link Props}对象原分组变为前缀
*
* @return Properties对象
* @return {@link Props}对象
*/
public Properties toProperties() {
final Properties properties = new Properties();
public Props toProps() {
final Props props = new Props();
String group;
for (final Entry<String, LinkedHashMap<String, String>> groupEntry : this.groupedMap.entrySet()) {
group = groupEntry.getKey();
for (final Entry<String, String> entry : groupEntry.getValue().entrySet()) {
properties.setProperty(StrUtil.isEmpty(group) ? entry.getKey() : group + CharUtil.DOT + entry.getKey(), entry.getValue());
props.setProperty(StrUtil.isEmpty(group) ? entry.getKey() : group + CharUtil.DOT + entry.getKey(), entry.getValue());
}
}
return properties;
return props;
}
/**

View File

@ -13,20 +13,19 @@
package org.dromara.hutool.setting.props;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.resource.Resource;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.io.resource.UrlResource;
import org.dromara.hutool.core.io.watch.SimpleWatcher;
import org.dromara.hutool.core.io.watch.WatchMonitor;
import org.dromara.hutool.core.io.watch.WatchUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.func.LambdaInfo;
import org.dromara.hutool.core.func.LambdaUtil;
import org.dromara.hutool.core.func.SerFunction;
import org.dromara.hutool.core.func.SerSupplier;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.resource.Resource;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.io.watch.SimpleWatcher;
import org.dromara.hutool.core.io.watch.WatchMonitor;
import org.dromara.hutool.core.io.watch.WatchUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.getter.TypeGetter;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.ConstructorUtil;
@ -102,6 +101,16 @@ public final class Props extends Properties implements TypeGetter<CharSequence>
return new Props(resource, charset);
}
/**
* {@link Properties}转为Props
*
* @param properties {@link Properties}
* @return Props
*/
public static Props of(final Properties properties) {
return new Props(properties);
}
// ----------------------------------------------------------------------- 构造方法 start
/**
@ -272,6 +281,37 @@ public final class Props extends Properties implements TypeGetter<CharSequence>
return (String) value;
}
/**
* 获取一个新的子属性子属性键值对拥有公共前缀.分隔
* <pre>
* a.b
* a.c
* b.a
* </pre>
* 则调用getSubProps("a");得到
* <pre>
* a.b
* a.c
* </pre>
*
* @param prefix 前缀可以不以.结尾
* @return 子属性
*/
public Props getSubProps(final String prefix) {
final Props subProps = new Props();
final String finalPrefix = StrUtil.addSuffixIfNot(prefix, StrUtil.DOT);
final int prefixLength = finalPrefix.length();
forEach((key, value) -> {
final String keyStr = key.toString();
if (StrUtil.startWith(keyStr, finalPrefix)) {
subProps.set(StrUtil.subSuf(keyStr, prefixLength), value);
}
});
return subProps;
}
/**
* 转换为标准的{@link Properties}对象
*
@ -325,7 +365,28 @@ public final class Props extends Properties implements TypeGetter<CharSequence>
*/
public <T> T toBean(final Class<T> beanClass, final String prefix) {
final T bean = ConstructorUtil.newInstanceIfPossible(beanClass);
return fillBean(bean, prefix);
return toBean(bean, prefix);
}
/**
* 将配置文件转换为Bean支持嵌套Bean<br>
* 支持的表达式
*
* <pre>
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
* </pre>
*
* @param <T> Bean类型
* @param bean Bean对象
* @return Bean对象
* @since 4.6.3
*/
public <T> T toBean(final T bean) {
return toBean(bean, null);
}
/**
@ -346,7 +407,7 @@ public final class Props extends Properties implements TypeGetter<CharSequence>
* @return Bean对象
* @since 4.6.3
*/
public <T> T fillBean(final T bean, String prefix) {
public <T> T toBean(final T bean, String prefix) {
prefix = StrUtil.emptyIfNull(StrUtil.addSuffixIfNot(prefix, StrUtil.DOT));
String key;

View File

@ -88,7 +88,7 @@ public class PropsTest {
Assertions.assertEquals(DateUtil.parse("2020-01-01"), systemConfig.getCreateTime());
Assertions.assertEquals(true, systemConfig.getIsInit());
Assertions.assertEquals("1", systemConfig.getStairPlan());
Assertions.assertEquals(new Integer(2), systemConfig.getStageNum());
Assertions.assertEquals(Integer.valueOf(2), systemConfig.getStageNum());
Assertions.assertEquals("3", systemConfig.getVersion());
}
@ -124,5 +124,16 @@ public class PropsTest {
private Date nextStageTime;//当前阶段结束日期/下一阶段开始日期
}
@Test
void getSubTest() {
final Props props = new Props();
props.set("a.b", "1");
props.set("a.c", "2");
props.set("b.a", "3");
final Props subProps = props.getSubProps("a");
Assertions.assertEquals(2, subProps.size());
Assertions.assertEquals("1", subProps.getStr("b"));
Assertions.assertEquals("2", subProps.getStr("c"));
}
}