getFreeConnectionFromPool() {
110 | for(IPooledConnection pc : pool) {
111 | if(!pc.isBusy()) {
112 | pc.setBusy(true);
113 | // LOG.debug("从连接池中获取连接");
114 |
115 | // 验证有效性
116 | if(testOnBorrow) {
117 | // LOG.debug("Test on borrow start");
118 | checkValid(pc);
119 | // LOG.debug("Test on borrow finish");
120 | }
121 |
122 | return Optional.of(pc);
123 | }
124 | }
125 | // 空
126 | return Optional.empty();
127 | }
128 |
129 |
130 | /**
131 | * https://stackoverflow.com/questions/3668506/efficient-sql-test-query-or-validation-query-that-will-work-across-all-or-most
132 | *
133 | * 真正支持标准的,直接使用 {@link Connection#isValid(int)} 验证比较合适
134 | * @param pooledConnection 连接池信息
135 | * @since 1.5.0
136 | */
137 | private void checkValid(final IPooledConnection pooledConnection) {
138 | if(StringUtil.isNotEmpty(super.validQuery)) {
139 | Connection connection = pooledConnection.getConnection();
140 | try {
141 | // 如果连接无效,重新申请一个新的替代
142 | if(!connection.isValid(super.validTimeOutSeconds)) {
143 | // LOG.debug("Old connection is inValid, start create one for it.");
144 |
145 | Connection newConnection = createConnection();
146 | pooledConnection.setConnection(newConnection);
147 | // LOG.debug("Old connection is inValid, finish create one for it.");
148 | }
149 | } catch (SQLException throwables) {
150 | LOG.error("[JdbcPool] checkValid failed", throwables);
151 |
152 | throw new JdbcPoolException(throwables);
153 | }
154 | } else {
155 | // LOG.debug("valid query is empty, ignore valid.");
156 | }
157 | }
158 |
159 | /**
160 | * 初始化连接池
161 | * @since 1.1.0
162 | */
163 | private void initJdbcPool() {
164 | final int minSize = super.minSize;
165 | pool = new ArrayList<>(minSize);
166 |
167 | for(int i = 0; i < minSize; i++) {
168 | IPooledConnection pooledConnection = createPooledConnection();
169 |
170 | pool.add(pooledConnection);
171 | }
172 | }
173 |
174 | /**
175 | * 创建一个池化的连接
176 | * @return 连接
177 | * @since 1.1.0
178 | */
179 | private IPooledConnection createPooledConnection() {
180 | Connection connection = createConnection();
181 |
182 | IPooledConnection pooledConnection = new PooledConnection();
183 | pooledConnection.setBusy(false);
184 | pooledConnection.setConnection(connection);
185 | pooledConnection.setDataSource(this);
186 |
187 | return pooledConnection;
188 | }
189 |
190 | /**
191 | * 创建新连接
192 | * @return 连接
193 | * @since 1.1.0
194 | */
195 | private Connection createConnection() {
196 | try {
197 | return DriverManager.getConnection(super.getJdbcUrl(),
198 | super.getUser(), super.getPassword());
199 | } catch (SQLException e) {
200 | LOG.error("[JdbcPool] createConnection failed", e);
201 |
202 | throw new JdbcPoolException(e);
203 | }
204 | }
205 |
206 |
207 | /**
208 | * 初始化空闲时检验
209 | * @since 1.5.0
210 | */
211 | private void initTestOnIdle() {
212 | if(StringUtil.isNotEmpty(validQuery)) {
213 | ScheduledExecutorService idleExecutor = Executors.newSingleThreadScheduledExecutor();
214 |
215 | idleExecutor.scheduleAtFixedRate(new Runnable() {
216 | @Override
217 | public void run() {
218 | testOnIdleCheck();
219 | }
220 | }, super.testOnIdleIntervalSeconds, testOnIdleIntervalSeconds, TimeUnit.SECONDS);
221 | // LOG.debug("Test on idle config with interval seconds: " + testOnIdleIntervalSeconds);
222 | }
223 | }
224 |
225 | /**
226 | * 验证所有的空闲连接是否有效
227 | * @since 1.5.0
228 | */
229 | private void testOnIdleCheck() {
230 | // LOG.debug("start check test on idle");
231 | for(IPooledConnection pc : this.pool) {
232 | if(!pc.isBusy()) {
233 | checkValid(pc);
234 | }
235 | }
236 | // LOG.debug("finish check test on idle");
237 | }
238 |
239 | }
240 |
--------------------------------------------------------------------------------
/src/main/java/com/github/houbb/thread/pool/datasource/UnPooledDataSource.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.datasource;
2 |
3 | import com.github.houbb.thread.pool.util.DriverClassUtil;
4 |
5 | import java.sql.Connection;
6 | import java.sql.DriverManager;
7 | import java.sql.SQLException;
8 |
9 | /**
10 | * @author binbin.hou
11 | * @since 1.0.0
12 | */
13 | public class UnPooledDataSource extends AbstractDataSourceConfig {
14 |
15 | @Override
16 | public Connection getConnection() throws SQLException {
17 | DriverClassUtil.loadDriverClass(super.driverClass, super.jdbcUrl);
18 |
19 | return DriverManager.getConnection(super.getJdbcUrl(),
20 | super.getUser(), super.getPassword());
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/github/houbb/thread/pool/exception/JdbcPoolException.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.exception;
2 |
3 | /**
4 | * @author binbin.hou
5 | * @since 1.0.0
6 | */
7 | public class JdbcPoolException extends RuntimeException {
8 |
9 | public JdbcPoolException() {
10 | }
11 |
12 | public JdbcPoolException(String message) {
13 | super(message);
14 | }
15 |
16 | public JdbcPoolException(String message, Throwable cause) {
17 | super(message, cause);
18 | }
19 |
20 | public JdbcPoolException(Throwable cause) {
21 | super(cause);
22 | }
23 |
24 | public JdbcPoolException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
25 | super(message, cause, enableSuppression, writableStackTrace);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/github/houbb/thread/pool/model/DataSourceConfigDto.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.model;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * 配置对象
7 | *
8 | * @since 1.7.0
9 | */
10 | public class DataSourceConfigDto implements Serializable {
11 |
12 | /**
13 | * 驱动类
14 | * @since 1.0.0
15 | */
16 | protected String driverClass;
17 |
18 | /**
19 | * jdbc url
20 | * @since 1.0.0
21 | */
22 | protected String jdbcUrl;
23 |
24 | /**
25 | * 用户
26 | * @since 1.0.0
27 | */
28 | protected String user;
29 |
30 | /**
31 | * 密码
32 | * @since 1.0.0
33 | */
34 | protected String password;
35 |
36 | public String getDriverClass() {
37 | return driverClass;
38 | }
39 |
40 | public void setDriverClass(String driverClass) {
41 | this.driverClass = driverClass;
42 | }
43 |
44 | public String getJdbcUrl() {
45 | return jdbcUrl;
46 | }
47 |
48 | public void setJdbcUrl(String jdbcUrl) {
49 | this.jdbcUrl = jdbcUrl;
50 | }
51 |
52 | public String getUser() {
53 | return user;
54 | }
55 |
56 | public void setUser(String user) {
57 | this.user = user;
58 | }
59 |
60 | public String getPassword() {
61 | return password;
62 | }
63 |
64 | public void setPassword(String password) {
65 | this.password = password;
66 | }
67 |
68 | @Override
69 | public String toString() {
70 | return "DataSourceConfigDto{" +
71 | "driverClass='" + driverClass + '\'' +
72 | ", jdbcUrl='" + jdbcUrl + '\'' +
73 | ", user='" + user + '\'' +
74 | ", password='" + password + '\'' +
75 | '}';
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/github/houbb/thread/pool/package-info.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool;
--------------------------------------------------------------------------------
/src/main/java/com/github/houbb/thread/pool/util/DriverClassUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.util;
2 |
3 | import com.github.houbb.heaven.util.common.ArgUtil;
4 | import com.github.houbb.heaven.util.lang.StringUtil;
5 | import com.github.houbb.thread.pool.exception.JdbcPoolException;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | /**
11 | * 驱动类工具
12 | *
13 | * 1. 默认添加对应的驱动类信息
14 | * project: jdbc-pool-JdbcDriveUtil
15 | * create on 2020/7/18 9:21
16 | *
17 | * @author binbin.hou
18 | * @since 1.2.0
19 | */
20 | public final class DriverClassUtil {
21 |
22 | /**
23 | * 存放驱动类信息
24 | * @since 1.2.0
25 | */
26 | private static final Map DRIVER_CLASS_MAP;
27 |
28 | static {
29 | DRIVER_CLASS_MAP = new HashMap<>(32);
30 | DRIVER_CLASS_MAP.put("jdbc:db2", "COM.ibm.db2.jdbc.app.DB2Driver");
31 | DRIVER_CLASS_MAP.put("jdbc:firebirdsql", "org.firebirdsql.jdbc.FBDriver");
32 | DRIVER_CLASS_MAP.put("jdbc:edbc", "ca.edbc.jdbc.EdbcDriver");
33 | DRIVER_CLASS_MAP.put("jdbc:pointbase", "com.pointbase.jdbc.jdbcUniversalDriver");
34 | DRIVER_CLASS_MAP.put("jdbc:fake", "com.alibaba.druid.mock.MockDriver");
35 | DRIVER_CLASS_MAP.put("jdbc:informix-sqli", "com.informix.jdbc.IfxDriver");
36 | DRIVER_CLASS_MAP.put("jdbc:sqlite", "org.sqlite.JDBC");
37 | DRIVER_CLASS_MAP.put("jdbc:microsoft", "com.microsoft.jdbc.sqlserver.SQLServerDriver");
38 | DRIVER_CLASS_MAP.put("jdbc:hsqldb", "org.hsqldb.jdbcDriver");
39 | DRIVER_CLASS_MAP.put("jdbc:postgresql", "org.postgresql.Driver");
40 | DRIVER_CLASS_MAP.put("jdbc:ingres", "com.ingres.jdbc.IngresDriver");
41 | DRIVER_CLASS_MAP.put("jdbc:cloudscape", "COM.cloudscape.core.JDBCDriver");
42 | DRIVER_CLASS_MAP.put("jdbc:JSQLConnect", "com.jnetdirect.jsql.JSQLDriver");
43 | DRIVER_CLASS_MAP.put("jdbc:derby", "org.apache.derby.jdbc.EmbeddedDriver");
44 | DRIVER_CLASS_MAP.put("jdbc:timesten", "com.timesten.jdbc.TimesTenDriver");
45 | DRIVER_CLASS_MAP.put("jdbc:interbase", "interbase.interclient.Driver");
46 | DRIVER_CLASS_MAP.put("jdbc:h2", "org.h2.Driver");
47 | DRIVER_CLASS_MAP.put("jdbc:as400", "com.ibm.as400.access.AS400JDBCDriver");
48 | DRIVER_CLASS_MAP.put("jdbc:sybase:Tds", "com.sybase.jdbc2.jdbc.SybDriver");
49 | DRIVER_CLASS_MAP.put("jdbc:mock", "com.alibaba.druid.mock.MockDriver");
50 | DRIVER_CLASS_MAP.put("jdbc:oracle", "oracle.jdbc.driver.OracleDriver");
51 | DRIVER_CLASS_MAP.put("jdbc:mysql", "com.mysql.cj.jdbc.Driver");
52 | DRIVER_CLASS_MAP.put("jdbc:odps", "com.aliyun.odps.jdbc.OdpsDriver");
53 | DRIVER_CLASS_MAP.put("jdbc:mckoi", "com.mckoi.JDBCDriver");
54 | DRIVER_CLASS_MAP.put("jdbc:jtds", "net.sourceforge.jtds.jdbc.Driver");
55 | DRIVER_CLASS_MAP.put("jdbc:sapdb", "com.sap.dbtech.jdbc.DriverSapDB");
56 | DRIVER_CLASS_MAP.put("jdbc:JTurbo", "com.newatlanta.jturbo.driver.Driver");
57 | DRIVER_CLASS_MAP.put("jdbc:mimer:multi1", "com.mimer.jdbc.Driver");
58 | }
59 |
60 | /**
61 | * 加载驱动类信息
62 | * @param driverClass 驱动类
63 | * @param url 连接信息
64 | * @since 1.2.0
65 | */
66 | public static void loadDriverClass(String driverClass, final String url) {
67 | ArgUtil.notEmpty(url, url);
68 |
69 | if(StringUtil.isEmptyTrim(driverClass)) {
70 | driverClass = getDriverClassByUrl(url);
71 | }
72 |
73 | try {
74 | Class.forName(driverClass);
75 | } catch (ClassNotFoundException e) {
76 | throw new JdbcPoolException(e);
77 | }
78 | }
79 |
80 |
81 | /**
82 | * 根据 URL 获取对应的驱动类
83 | *
84 | * 1. 禁止 url 为空
85 | * 2. 如果未找到,则直接报错。
86 | * @param url url
87 | * @return 驱动信息
88 | */
89 | private static String getDriverClassByUrl(final String url) {
90 | ArgUtil.notEmpty(url, "url");
91 |
92 | for(Map.Entry entry : DRIVER_CLASS_MAP.entrySet()) {
93 | String urlPrefix = entry.getKey();
94 | if(url.startsWith(urlPrefix)) {
95 | return entry.getValue();
96 | }
97 | }
98 |
99 | throw new JdbcPoolException("Can't auto find match driver class for url: " + url);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/test/java/com/github/houbb/thread/pool/datasource/GenTest.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.datasource;
2 |
3 | import com.github.houbb.heaven.util.io.FileUtil;
4 | import org.junit.Ignore;
5 | import org.junit.Test;
6 |
7 | import java.util.Map;
8 |
9 | /**
10 | * project: jdbc-pool-GenTest
11 | * create on 2020/7/18 9:25
12 | *
13 | * @author binbin.hou
14 | * @since 1.2.0
15 | */
16 | @Ignore
17 | public class GenTest {
18 |
19 | @Test
20 | public void driverClassTest() {
21 | Map map = FileUtil.readToMap("D:\\_github\\jdbc-pool\\src\\test\\resources\\driverClass.txt", " ");
22 |
23 | final String format = "DRIVER_CLASS_MAP.put(\"%s\", \"%s\");";
24 | for(Map.Entry entry : map.entrySet()) {
25 | String result = String.format(format, entry.getKey(), entry.getValue());
26 | System.out.println(result);
27 | }
28 | }
29 |
30 | @Test
31 | public void driverClassMDTest() {
32 | Map map = FileUtil.readToMap("D:\\_github\\jdbc-pool\\src\\test\\resources\\driverClass.txt", " ");
33 |
34 | final String format = "| %s | %s |";
35 | for(Map.Entry entry : map.entrySet()) {
36 | String result = String.format(format, entry.getKey(), entry.getValue());
37 | System.out.println(result);
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/com/github/houbb/thread/pool/datasource/JdbcPoolbsTest.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.datasource;
2 |
3 | import com.github.houbb.thread.pool.bs.JdbcPoolBs;
4 | import org.junit.Ignore;
5 | import org.junit.Test;
6 |
7 | import javax.sql.DataSource;
8 |
9 | /**
10 | * project: jdbc-pool-GenTest
11 | * create on 2020/7/18 9:25
12 | *
13 | * @author binbin.hou
14 | * @since 1.2.0
15 | */
16 | @Ignore
17 | public class JdbcPoolbsTest {
18 |
19 | @Test
20 | public void bsTest() {
21 | JdbcPoolBs jdbcPoolBs = JdbcPoolBs.newInstance()
22 | .username("root")
23 | .password("123456")
24 | .url("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
25 |
26 | DataSource pooled = jdbcPoolBs.pooled();
27 | DataSource unPooled = jdbcPoolBs.unPooled();
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/java/com/github/houbb/thread/pool/datasource/PooledDataSourceTest.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.datasource;
2 |
3 | import com.github.houbb.heaven.util.util.DateUtil;
4 | import com.github.houbb.thread.pool.exception.JdbcPoolException;
5 | import org.junit.Test;
6 |
7 | import java.sql.Connection;
8 | import java.sql.SQLException;
9 |
10 | /**
11 | * @author binbin.hou
12 | * @since 1.1.0
13 | */
14 | public class PooledDataSourceTest {
15 |
16 | @Test
17 | public void simpleTest() throws SQLException {
18 | PooledDataSource source = new PooledDataSource();
19 | source.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
20 | source.setUser("root");
21 | source.setPassword("123456");
22 | source.setMinSize(1);
23 |
24 | // 初始化
25 | source.init();
26 |
27 | Connection connection = source.getConnection();
28 | System.out.println(connection.getCatalog());
29 |
30 | Connection connection2 = source.getConnection();
31 | System.out.println(connection2.getCatalog());
32 | }
33 |
34 | @Test
35 | public void notWaitTest() throws SQLException {
36 | PooledDataSource source = new PooledDataSource();
37 | source.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
38 | source.setUser("root");
39 | source.setPassword("123456");
40 | source.setMinSize(1);
41 | source.setMaxSize(1);
42 | source.setMaxWaitMills(0);
43 |
44 | // 初始化
45 | source.init();
46 |
47 | Connection connection = source.getConnection();
48 | System.out.println(connection.getCatalog());
49 |
50 | // 新的线程执行
51 | newThreadExec(source);
52 |
53 | DateUtil.sleep(100);
54 | }
55 |
56 | private void newThreadExec(final PooledDataSource source) {
57 | // 另起一个线程
58 | new Thread(new Runnable() {
59 | @Override
60 | public void run() {
61 | // 预期报错
62 | Connection connection2 = null;
63 | try {
64 | connection2 = source.getConnection();
65 | System.out.println(connection2.getCatalog());
66 | } catch (SQLException e) {
67 | throw new JdbcPoolException(e);
68 | }
69 | }
70 | }).start();
71 | }
72 |
73 | @Test
74 | public void waitTest() throws SQLException {
75 | PooledDataSource source = new PooledDataSource();
76 | source.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
77 | source.setUser("root");
78 | source.setPassword("123456");
79 | source.setMinSize(1);
80 | source.setMaxSize(1);
81 | source.setMaxWaitMills(100);
82 |
83 | // 初始化
84 | source.init();
85 |
86 | Connection connection = source.getConnection();
87 | System.out.println(connection.getCatalog());
88 |
89 | // 新的线程执行
90 | newThreadExec(source);
91 |
92 | DateUtil.sleep(10);
93 | connection.close();
94 | System.out.println("释放第一个线程的资源。。。");
95 |
96 | DateUtil.sleep(100);
97 | }
98 |
99 | @Test
100 | public void testOnIdleTest() throws SQLException {
101 | PooledDataSource source = new PooledDataSource();
102 | source.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
103 | source.setUser("root");
104 | source.setPassword("123456");
105 | source.setTestOnIdleIntervalSeconds(5);
106 |
107 | // 初始化配置
108 | source.init();
109 |
110 | Connection connection = source.getConnection();
111 | System.out.println(connection.getCatalog());
112 |
113 | DateUtil.sleep(30 * 1000);
114 |
115 | connection.close();
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/src/test/java/com/github/houbb/thread/pool/datasource/UnPooledDataSourceTest.java:
--------------------------------------------------------------------------------
1 | package com.github.houbb.thread.pool.datasource;
2 |
3 | import org.junit.Test;
4 |
5 | import java.sql.Connection;
6 | import java.sql.SQLException;
7 |
8 | /**
9 | * @author binbin.hou
10 | * @since 1.0.0
11 | */
12 | public class UnPooledDataSourceTest {
13 |
14 | @Test
15 | public void simpleTest() throws SQLException {
16 | UnPooledDataSource source = new UnPooledDataSource();
17 | source.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
18 | source.setUser("root");
19 | source.setPassword("123456");
20 |
21 | Connection connection = source.getConnection();
22 | System.out.println(connection.getCatalog());
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/resources/driverClass.txt:
--------------------------------------------------------------------------------
1 | jdbc:odps com.aliyun.odps.jdbc.OdpsDriver
2 | jdbc:derby org.apache.derby.jdbc.EmbeddedDriver
3 | jdbc:mysql com.mysql.jdbc.Driver
4 | jdbc:oracle oracle.jdbc.driver.OracleDriver
5 | jdbc:microsoft com.microsoft.jdbc.sqlserver.SQLServerDriver
6 | jdbc:sybase:Tds com.sybase.jdbc2.jdbc.SybDriver
7 | jdbc:jtds net.sourceforge.jtds.jdbc.Driver
8 | jdbc:postgresql org.postgresql.Driver
9 | jdbc:fake com.alibaba.druid.mock.MockDriver
10 | jdbc:mock com.alibaba.druid.mock.MockDriver
11 | jdbc:hsqldb org.hsqldb.jdbcDriver
12 | jdbc:db2 COM.ibm.db2.jdbc.app.DB2Driver
13 | jdbc:sqlite org.sqlite.JDBC
14 | jdbc:ingres com.ingres.jdbc.IngresDriver
15 | jdbc:h2 org.h2.Driver
16 | jdbc:mckoi com.mckoi.JDBCDriver
17 | jdbc:cloudscape COM.cloudscape.core.JDBCDriver
18 | jdbc:informix-sqli com.informix.jdbc.IfxDriver
19 | jdbc:timesten com.timesten.jdbc.TimesTenDriver
20 | jdbc:as400 com.ibm.as400.access.AS400JDBCDriver
21 | jdbc:sapdb com.sap.dbtech.jdbc.DriverSapDB
22 | jdbc:JSQLConnect com.jnetdirect.jsql.JSQLDriver
23 | jdbc:JTurbo com.newatlanta.jturbo.driver.Driver
24 | jdbc:firebirdsql org.firebirdsql.jdbc.FBDriver
25 | jdbc:interbase interbase.interclient.Driver
26 | jdbc:pointbase com.pointbase.jdbc.jdbcUniversalDriver
27 | jdbc:edbc ca.edbc.jdbc.EdbcDriver
28 | jdbc:mimer:multi1 com.mimer.jdbc.Driver
--------------------------------------------------------------------------------