【jdbc編程】使用c3p0連接池對MySQL數據庫進行訪問

介紹

連接池是創建和管理一個連接的緩衝池的技術,這些連接準備好被任何需要它們的線程使用。即如果有大量用戶訪問數據庫,連接池便可以爲每個用戶提供連接,用戶訪問完之後收回連接以備再次使用。

連接池可以極大的改善用戶的 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
421, 2020 3:01:25 下午 com.mchange.v2.log.MLog 
信息: MLog clients using java 1.4+ standard logging.
421, 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.
421, 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用戶類中,我們還可以建立不同的用戶類,分爲管理員用戶、普通用戶等,不同用戶中提供不同的操作函數,如管理員可以進行增刪改查,普通用戶可以查看和添加,遊客用戶只可以查看等,此處不再實現,因爲並不是本文重點。

這就是本文的全部內容

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章