介紹
連接池是創建和管理一個連接的緩衝池的技術,這些連接準備好被任何需要它們的線程使用。即如果有大量用戶訪問數據庫,連接池便可以爲每個用戶提供連接,用戶訪問完之後收回連接以備再次使用。
連接池可以極大的改善用戶的 Java 應用程序的性能,同時減少全部資源的使用。
c3p0連接池下載地址
在這裏我選擇用c3p0作爲連接池。c3p0可以以下鏈接進行下載:c3p0下載
初始化連接池
package hellomysql;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
public class DataSource {
public DataSource(String user, String password) {
try {
//初始化連接池
dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setUser(user);
dataSource.setPassword(password);
//指定初始連接數爲10
dataSource.setInitialPoolSize(10);
//指定最大連接數爲50
dataSource.setMaxPoolSize(50);
} catch (PropertyVetoException event) {
event.printStackTrace();
}
}
public void setURL(String URL) {
dataSource.setJdbcUrl(URL);
}
public Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//使用ComboPooledDataSource類型作爲連接池
private ComboPooledDataSource dataSource;
}
通過這個類,我們就可以創建一個連接池,並從中得到一個連接:
package hellomysql;
public class Main {
public static void main(String[] args) {
try {
DataSource dataSource = new DataSource("root","root");
dataSource.setURL("jdbc:mysql://localhost:3306/hello_mysql");
Connection connection = dataSource.getConnection();
} catch (Exception event) {
event.printStackTrace();
}
}
}
異常:修復時區錯誤
但我在實際操作時會報異常:java.sql.SQLException: The server time zone value is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
爲解決此問題,我們要在URL內設置時區爲標準世界時間,同時,最好將編碼格式設爲utf-8以便於中文讀取。
我們把鏈接改爲如下樣式便可以解決問題:
connector.setURL("jdbc:mysql://localhost:3306/hello_mysql?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
指定數據表
在實現DataSource類後,我們再創建一個Connector類,這樣就可以在Connector類中指定對應的數據表並獲取表頭。
package hellomysql;
import java.sql.*;
import java.util.ArrayList;
public class Connector extends DataSource {
//設置登錄MySQL數據庫的用戶名和密碼
public Connector(String user, String password) {
//調用super訪問父類的構造函數
super(user, password);
System.out.println("Welcome " + user + "!");
}
//指定要訪問數據庫中的哪個數據表
public void setTable(String table) throws Exception {
//用String類存儲表名
this.table = table;
System.out.println("Table set success! The table is: " + table + ".");
Connection connection = super.getConnection();
//獲取指定數據表的表頭
setHeader(connection);
}
public String getTable() {
return table;
}
public ArrayList<String> getHeaders() {
return headers;
}
//獲取表頭
private void setHeader(Connection connection) throws Exception {
String sql = String.format("select * from %s", table);
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
headers.clear();
//遍歷表頭行的每一個表頭,並用ArrayList<String>類存儲
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); ++i)
headers.add(resultSet.getMetaData().getColumnName(i));
}
private String table;
private ArrayList<String> headers = new ArrayList<>();
}
這樣,我們就可以創建一個Connector類,併爲其指定用戶名、密碼、URL、數據表名:
package hellomysql;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
try {
Connector connector = new Connector("root", "123");
connector.setURL("jdbc:mysql://localhost:3306/hello_mysql?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
connector.setTable("people");
} catch (Exception event) {
event.printStackTrace();
}
}
}
獲取連接
接下來,我們就可以從Connector中返回連接分配給指定用戶,用戶就可以對數據表進行瀏覽和增刪改查操作,我們再創建一個ConnectionUser用戶類:
package hellomysql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class ConnectionUser {
//構造函數,初始化時指定Connector
public ConnectionUser(Connector connector) {
connection = connector.getConnection();
this.connector = connector;
}
//用於遍歷數據表,數據表信息從Connector中獲取
public void viewData() throws Exception {
String sql = String.format("select * from %s", connector.getTable());
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println("----------------------------");
System.out.println(connector.getHeaders());
//遍歷輸出每一行
while (resultSet.next()) {
for (String each : connector.getHeaders()) {
System.out.print(resultSet.getString(each) + ", ");
}
//刪除最後一行的逗號和空格
System.out.print("\b\b");
System.out.println();
}
System.out.println("----------------------------");
System.out.println("View success!");
}
//重載另一個遍歷數據表函數,這個函數可以通過指定表名來獲取對應列
public void viewData(String... dataType) throws Exception {
String sql = String.format("select * from %s", connector.getTable());
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println("----------------------------");
for (String each : dataType) {
System.out.print(each + " ");
}
System.out.println();
while (resultSet.next()) {
for (String each : dataType) {
System.out.print(resultSet.getString(each) + ", ");
}
System.out.print("\b\b");
System.out.println();
}
System.out.println("----------------------------");
System.out.println("View success!");
}
//關閉連接
public void stopConnection() throws Exception {
if (connection != null)
connection.close();
}
//存儲指定的Connector,以獲取用戶所要訪問的URL和數據表信息
private Connector connector;
//存儲從DataSource內獲取的連接
private Connection connection;
}
這樣,我們就可以創建很多用戶並對數據庫進行訪問了:
package hellomysql;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
try {
Connector connector = new Connector("root", "123");
connector.setURL("jdbc:mysql://localhost:3306/hello_mysql?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
connector.setTable("people");
//創建一個ArrayList類來存儲用戶
ArrayList<ConnectionUser> users = new ArrayList<>();
//創建10個用戶
for (int i = 0; i < 10; ++i) {
users.add(new ConnectionUser(connector));
}
//每個用戶都進行查詢,以模擬10個用戶同時訪問數據庫的情形
for (ConnectionUser each : users) {
each.viewData();
}
//關閉每個用戶的連接,之所以不在上一個循環體內關閉,
//是爲了模擬多個用戶的同時訪問,以測試c3p0連接池的使用效果
for (ConnectionUser each : users) {
each.stopConnection();
}
} catch (Exception event) {
event.printStackTrace();
}
}
}
運行效果
效果如下:
D:\software\system\IdeaIU-2019.3.3\jdk\jdk-14\bin\java.exe -javaagent:D:\software\system\IdeaIU-2019.3.3\lib\idea_rt.jar=53693:D:\software\system\IdeaIU-2019.3.3\bin -Dfile.encoding=UTF-8 -classpath D:\programme\java\out\production\java;D:\software\system\mysql-8.0.19-winx64\lib\mysql-connector-java-8.0.19.jar;D:\software\system\mysql-8.0.19-winx64\lib\c3p0-0.9.5.5.jar;D:\software\system\mysql-8.0.19-winx64\lib\mchange-commons-java-0.2.19.jar hellomysql.Main
4月 21, 2020 3:01:25 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
4月 21, 2020 3:01:26 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5.5 [built 11-December-2019 22:07:46 -0800; debug? true; trace: 10]
Welcome root!
Table set success! The table is: people.
4月 21, 2020 3:01:26 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hgeby9a9175nsjevsaq70|129a8472, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.cj.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hgeby9a9175nsjevsaq70|129a8472, idleConnectionTestPeriod -> 0, initialPoolSize -> 10, jdbcUrl -> jdbc:mysql://localhost:3306/hello_mysql?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 50, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
----------------------------
[id, name, age]
1, Adam, 13
2, Amy, 18
3, Cathy, 15
4, Henry, 21
5, Hans, 17
----------------------------
View success!
----------------------------
[id, name, age]
1, Adam, 13
2, Amy, 18
3, Cathy, 15
4, Henry, 21
5, Hans, 17
----------------------------
View success!
//......實際操作中會生成10次查表信息,此處省略以節省篇幅
----------------------------
[id, name, age]
1, Adam, 13
2, Amy, 18
3, Cathy, 15
4, Henry, 21
5, Hans, 17
----------------------------
View success!
Process finished with exit code 0
經測試,本文中的連接池大概支持20個用戶的同時訪問,在實際使用中,我們可以修改連接池設置以實現數量更大的用戶訪問。
此例中用戶只實現了查詢操作,當然增刪改查的函數也可以寫到ConnectionUser用戶類中,我們還可以建立不同的用戶類,分爲管理員用戶、普通用戶等,不同用戶中提供不同的操作函數,如管理員可以進行增刪改查,普通用戶可以查看和添加,遊客用戶只可以查看等,此處不再實現,因爲並不是本文重點。
這就是本文的全部內容