This commit is contained in:
Looly 2023-12-18 03:27:49 +08:00
parent 883441a113
commit 7ad4f5f850
10 changed files with 114 additions and 317 deletions

View File

@ -28,4 +28,11 @@ public interface Poolable<T> extends Wrapper<T> {
* @return 最后借出时间
*/
long getLastBorrow();
/**
* 设置最后借出时间在成功借出此对象时更新时间
*
* @param lastBorrow 最后借出时间
*/
void setLastBorrow(final long lastBorrow);
}

View File

@ -113,7 +113,7 @@ public class PartitionObjectPool<T> implements ObjectPool<T> {
* @param poolConfig 池配置
* @return 队列
*/
protected BlockingQueue<PartitionPoolable<T>> createBlockingQueue(final PartitionPoolConfig poolConfig) {
protected BlockingQueue<Poolable<T>> createBlockingQueue(final PartitionPoolConfig poolConfig) {
return new ArrayBlockingQueue<>(poolConfig.getMaxSize());
}

View File

@ -54,12 +54,8 @@ public class PartitionPoolable<T> implements Poolable<T> {
return lastBorrow;
}
/**
* 设置最后借出时间在成功借出此对象时更新时间
*
* @param lastBorrow 最后借出时间
*/
protected void setLastBorrow(final long lastBorrow) {
@Override
public void setLastBorrow(final long lastBorrow) {
this.lastBorrow = lastBorrow;
}
}

View File

@ -48,7 +48,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
private final PoolConfig config;
private final ObjectFactory<T> objectFactory;
private BlockingQueue<PartitionPoolable<T>> queue;
private BlockingQueue<Poolable<T>> queue;
// 记录对象总数包括借出对象
private int total;
@ -59,7 +59,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
* @param queue 阻塞队列类型
* @param objectFactory 对象工厂用于管理对象创建检查和销毁
*/
public PoolPartition(final PoolConfig config, final BlockingQueue<PartitionPoolable<T>> queue, final ObjectFactory<T> objectFactory) {
public PoolPartition(final PoolConfig config, final BlockingQueue<Poolable<T>> queue, final ObjectFactory<T> objectFactory) {
this.config = config;
this.queue = queue;
this.objectFactory = objectFactory;
@ -73,15 +73,15 @@ public class PoolPartition<T> implements ObjectPool<T> {
@SuppressWarnings("resource")
@Override
public PartitionPoolable<T> borrowObject() {
public Poolable<T> borrowObject() {
// 非阻塞获取
PartitionPoolable<T> poolable = this.queue.poll();
Poolable<T> poolable = this.queue.poll();
if (null != poolable) {
// 检查对象是否可用
if (this.objectFactory.validate(poolable.getRaw())) {
// 检查是否超过最长空闲时间
final long maxIdle = this.config.getMaxIdle();
if (maxIdle > 0 && (System.currentTimeMillis() - poolable.getLastBorrow()) <= maxIdle) {
if (maxIdle <= 0 || (System.currentTimeMillis() - poolable.getLastBorrow()) <= maxIdle) {
poolable.setLastBorrow(System.currentTimeMillis());
return poolable;
}
@ -120,7 +120,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
// 检查对象可用性
if (this.objectFactory.validate(poolable.getRaw())) {
try {
this.queue.put((PartitionPoolable<T>) poolable);
this.queue.put(poolable);
} catch (final InterruptedException e) {
throw new HutoolException(e);
}
@ -194,7 +194,12 @@ public class PoolPartition<T> implements ObjectPool<T> {
*
* @return {@link PartitionPoolable}
*/
protected PartitionPoolable<T> createPoolable() {
@SuppressWarnings("unchecked")
protected Poolable<T> createPoolable() {
final T t = objectFactory.create();
if (t instanceof Poolable) {
return (Poolable<T>) t;
}
return new PartitionPoolable<>(objectFactory.create(), this);
}
@ -205,7 +210,7 @@ public class PoolPartition<T> implements ObjectPool<T> {
* @return 取出的池对象
* @throws HutoolException 中断异常
*/
private PartitionPoolable<T> waitingPoll() throws HutoolException {
private Poolable<T> waitingPoll() throws HutoolException {
final long maxWait = this.config.getMaxWait();
try {
if (maxWait <= 0) {

View File

@ -1,97 +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.core.map.MapUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.db.driver.DriverUtil;
import org.dromara.hutool.db.ds.DSKeys;
import org.dromara.hutool.setting.Setting;
/**
* 数据库配置文件类此类对应一个数据库配置文件
*
* @author Looly
*
*/
public class DbSetting {
/** 默认的数据库连接配置文件路径 */
public final static String DEFAULT_DB_CONFIG_PATH = "config/db.setting";
private final Setting setting;
/**
* 构造
*/
public DbSetting() {
this(null);
}
/**
* 构造
*
* @param setting 数据库配置
*/
public DbSetting(final Setting setting) {
if (null == setting) {
this.setting = new Setting(DEFAULT_DB_CONFIG_PATH);
} else {
this.setting = setting;
}
}
/**
* 获得数据库连接信息
*
* @param group 分组
* @return 分组
*/
public PooledDbConfig getDbConfig(final String group) {
final Setting config = setting.getSetting(group);
if (MapUtil.isEmpty(config)) {
throw new DbRuntimeException("No Hutool pool config for group: [{}]", group);
}
final PooledDbConfig pooledDbConfig = new PooledDbConfig();
// 基本信息
final String url = config.getAndRemove(DSKeys.KEY_ALIAS_URL);
if (StrUtil.isBlank(url)) {
throw new DbRuntimeException("No JDBC URL for group: [{}]", group);
}
pooledDbConfig.setUrl(url);
// 自动识别Driver
final String driver = config.getAndRemove(DSKeys.KEY_ALIAS_DRIVER);
pooledDbConfig.setDriver(StrUtil.isNotBlank(driver) ? driver : DriverUtil.identifyDriver(url));
pooledDbConfig.setUser(config.getAndRemove(DSKeys.KEY_ALIAS_USER));
pooledDbConfig.setPass(config.getAndRemove(DSKeys.KEY_ALIAS_PASSWORD));
// 连接池相关信息
pooledDbConfig.setInitialSize(setting.getIntByGroup("initialSize", group, 0));
pooledDbConfig.setMinIdle(setting.getIntByGroup("minIdle", group, 0));
pooledDbConfig.setMaxActive(setting.getIntByGroup("maxActive", group, 8));
pooledDbConfig.setMaxWait(setting.getLongByGroup("maxWait", group, 6000L));
// remarks等特殊配置since 5.3.8
String connValue;
for (final String key : DSKeys.KEY_CONN_PROPS) {
connValue = config.get(key);
if(StrUtil.isNotBlank(connValue)){
pooledDbConfig.addConnProps(key, connValue);
}
}
return pooledDbConfig;
}
}

View File

@ -12,8 +12,9 @@
package org.dromara.hutool.db.ds.pooled;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.pool.Poolable;
import org.dromara.hutool.db.DbRuntimeException;
import org.dromara.hutool.setting.props.Props;
import java.sql.Connection;
@ -26,21 +27,20 @@ import java.util.Properties;
*
* @author Looly
*/
public class PooledConnection extends ConnectionWrapper {
public class PooledConnection extends ConnectionWrapper implements Poolable<Connection> {
private final PooledDataSource ds;
private boolean isClosed;
private final PooledDataSource dataSource;
private long lastBorrow = System.currentTimeMillis();
private boolean isClosed = false;
/**
* 构造
*
* @param ds 数据源
* @throws SQLException SQL异常
* @param config 数据库配置
* @param dataSource 数据源
*/
public PooledConnection(final PooledDataSource ds) throws SQLException {
this.ds = ds;
final PooledDbConfig config = ds.getConfig();
public PooledConnection(final PooledDbConfig config, final PooledDataSource dataSource) {
final Props info = new Props();
final String user = config.getUser();
if (user != null) {
@ -57,56 +57,33 @@ public class PooledConnection extends ConnectionWrapper {
info.putAll(connProps);
}
this.raw = DriverManager.getConnection(config.getUrl(), info);
try {
this.raw = DriverManager.getConnection(config.getUrl(), info);
} catch (final SQLException e) {
throw new DbRuntimeException(e);
}
this.dataSource = dataSource;
}
/**
* 构造
*
* @param ds {@link PooledDataSource}
* @param conn {@link Connection}
*/
public PooledConnection(final PooledDataSource ds, final Connection conn) {
this.ds = ds;
this.raw = conn;
}
/**
* 重写关闭连接实际操作是归还到连接池中
*/
@Override
public void close() {
this.ds.free(this);
this.isClosed = true;
dataSource.returnObject(this);
}
/**
* 连接是否关闭关闭条件<br>
* 1被归还到池中
* 2实际连接已关闭
*/
@Override
public boolean isClosed() throws SQLException {
return isClosed || raw.isClosed();
public boolean isClosed() {
return this.isClosed;
}
/**
* 打开连接
*
* @return this
*/
protected PooledConnection open() {
this.isClosed = false;
return this;
@Override
public long getLastBorrow() {
return lastBorrow;
}
/**
* 释放连接
*
* @return this
*/
protected PooledConnection release() {
IoUtil.closeQuietly(this.raw);
return this;
@Override
public void setLastBorrow(final long lastBorrow) {
this.lastBorrow = lastBorrow;
}
}

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,102 +12,45 @@
package org.dromara.hutool.db.ds.pooled;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.thread.ThreadUtil;
import org.dromara.hutool.core.pool.ObjectFactory;
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.simple.AbstractDataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Queue;
/**
* 池化数据源
* 池化数据源用于管理数据库连接
*
* @author Looly
*
* @since 6.0.0
*/
public class PooledDataSource extends AbstractDataSource {
private Queue<PooledConnection> freePool;
private int activeCount; // 活跃连接数
private final PooledDbConfig config;
/**
* 获得一个数据源
*
* @param group 数据源分组
* @return {@code PooledDataSource}
*/
synchronized public static PooledDataSource getDataSource(final String group) {
return new PooledDataSource(group);
}
/**
* 获得一个数据源使用空分组
*
* @return {@code PooledDataSource}
*/
synchronized public static PooledDataSource getDataSource() {
return new PooledDataSource();
}
// -------------------------------------------------------------------- Constructor start
/**
* 构造读取默认的配置文件和默认分组
*/
public PooledDataSource() {
this(StrUtil.EMPTY);
}
/**
* 构造读取默认的配置文件
*
* @param group 分组
*/
public PooledDataSource(final String group) {
this(new DbSetting(), group);
}
private final ObjectPool<Connection> connPool;
/**
* 构造
*
* @param setting 数据库配置文件对象
* @param group 分组
*/
public PooledDataSource(final DbSetting setting, final String group) {
this(setting.getDbConfig(group));
}
/**
* 构造
*
* @param config 数据库配置
* @param config 数据库池配置
*/
public PooledDataSource(final PooledDbConfig config) {
this.config = config;
freePool = new LinkedList<>();
int initialSize = config.getInitialSize();
try {
while (initialSize-- > 0) {
freePool.offer(newConnection());
}
} catch (final SQLException e) {
throw new DbRuntimeException(e);
}
}
// -------------------------------------------------------------------- Constructor start
final PartitionPoolConfig poolConfig = (PartitionPoolConfig) PartitionPoolConfig.of()
.setPartitionSize(1)
.setMaxWait(config.getMaxWait())
.setMinSize(config.getInitialSize())
.setMaxSize(config.getMaxActive());
this.connPool = new PartitionObjectPool<>(poolConfig, createConnFactory(config));
}
/**
* 从数据库连接池中获取数据库连接对象
*/
@Override
public synchronized Connection getConnection() throws SQLException {
return getConnection(config.getMaxWait());
public Connection getConnection() throws SQLException {
return (Connection) connPool.borrowObject();
}
@Override
@ -115,84 +58,46 @@ public class PooledDataSource extends AbstractDataSource {
throw new SQLException("Pooled DataSource is not allow to get special Connection!");
}
/**
* 释放连接连接会被返回给连接池
*
* @param conn 连接
* @return 释放成功与否
*/
protected synchronized boolean free(final PooledConnection conn) {
activeCount--;
return freePool.offer(conn);
}
/**
* 创建新连接
*
* @return 新连接
* @throws SQLException SQL异常
*/
public PooledConnection newConnection() throws SQLException {
return new PooledConnection(this);
}
public PooledDbConfig getConfig() {
return config;
}
/**
* 获取连接对象
*
* @param wait 当池中无连接等待的毫秒数
* @return 连接对象
* @throws SQLException SQL异常
*/
public PooledConnection getConnection(final long wait) throws SQLException {
try {
return getConnectionDirect();
} catch (final Exception e) {
ThreadUtil.sleep(wait);
}
return getConnectionDirect();
}
@Override
synchronized public void close() {
if (CollUtil.isNotEmpty(this.freePool)) {
this.freePool.forEach(PooledConnection::release);
this.freePool.clear();
this.freePool = null;
}
}
@Override
protected void finalize() {
IoUtil.closeQuietly(this);
public void close() {
IoUtil.closeQuietly(this.connPool);
}
/**
* 直接从连接池中获取连接如果池中无连接直接抛出异常
* 将连接返回到池中
*
* @return PooledConnection
* @throws SQLException SQL异常
* @param conn {@link PooledConnection}
*/
private PooledConnection getConnectionDirect() throws SQLException {
if (null == freePool) {
throw new SQLException("PooledDataSource is closed!");
}
final int maxActive = config.getMaxActive();
if (maxActive <= 0 || maxActive < this.activeCount) {
// 超过最大使用限制
throw new SQLException("In used Connection is more than Max Active.");
}
PooledConnection conn = freePool.poll();
if (null == conn || conn.open().isClosed()) {
conn = this.newConnection();
}
activeCount++;
return conn;
public void returnObject(final PooledConnection conn) {
this.connPool.returnObject(conn);
}
/**
* 创建自定义的{@link PooledConnection}工厂类
*
* @param config 数据库配置
* @return {@link ObjectFactory}
*/
private ObjectFactory<Connection> createConnFactory(final PooledDbConfig config) {
return new ObjectFactory<Connection>() {
@Override
public Connection create() {
return new PooledConnection(config, PooledDataSource.this);
}
@Override
public boolean validate(final Connection connection) {
try {
return null != connection && connection.isValid((int) config.getMaxWait());
} catch (final SQLException e) {
throw new DbRuntimeException(e);
}
}
@Override
public void destroy(final Connection connection) {
IoUtil.closeQuietly(connection);
}
};
}
}

View File

@ -24,10 +24,11 @@ import java.util.logging.Logger;
/**
* 数据源抽象实现
* @author Looly
*
* @author Looly
*/
public abstract class AbstractDataSource implements DataSource, Cloneable, Closeable{
public abstract class AbstractDataSource implements DataSource, Cloneable, Closeable {
@Override
public PrintWriter getLogWriter() {
return DriverManager.getLogWriter();
@ -60,6 +61,7 @@ public abstract class AbstractDataSource implements DataSource, Cloneable, Close
/**
* Support from JDK7
*
* @since 1.7
*/
@Override

View File

@ -12,6 +12,7 @@
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;
@ -20,7 +21,8 @@ public class DataSourceWrapperTest {
@Test
public void cloneTest(){
final SimpleDataSource simpleDataSource = new SimpleDataSource("jdbc:sqlite:test.db", "", "");
final SimpleDataSource simpleDataSource = new SimpleDataSource(
new DbConfig("jdbc:sqlite:test.db", "", ""));
final DSWrapper wrapper = new DSWrapper(simpleDataSource, "test.driver");
final DSWrapper clone = wrapper.clone();

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,11 +10,12 @@
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.db;
package org.dromara.hutool.db.ds;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.db.ds.DSUtil;
import org.dromara.hutool.db.ds.DSWrapper;
import org.dromara.hutool.db.Db;
import org.dromara.hutool.db.Entity;
import org.dromara.hutool.db.ds.bee.BeeDSFactory;
import org.dromara.hutool.db.ds.c3p0.C3p0DSFactory;
import org.dromara.hutool.db.ds.dbcp.DbcpDSFactory;
@ -22,7 +23,6 @@ import org.dromara.hutool.db.ds.druid.DruidDSFactory;
import org.dromara.hutool.db.ds.hikari.HikariDSFactory;
import org.dromara.hutool.db.ds.pooled.PooledDSFactory;
import org.dromara.hutool.db.ds.tomcat.TomcatDSFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;