專案常需同時支援多種資料庫。採用工廠模式封裝連線建立邏輯,可統一介面、隔離差異、便於擴充。本文提供一個簡潔的 ConnectDB 工具類,支援 MySQL 與 Oracle,並示範資源正確釋放。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectDB {
private static final String MYSQL_PREFIX = "jdbc:mysql://";
private static final String ORACLE_PREFIX = "jdbc:oracle:thin:@";
private ConnectDB() {} // 防止實例化
/**
* 統一獲取連線入口
* @param dbType "mysql" 或 "oracle"(不區分大小寫)
* @param url 不含前綴的連線字串,例如:
* MySQL: "localhost:3306/mydb?useSSL=false&serverTimezone=UTC"
* Oracle: "localhost:1521:ORCL"
* @return 資料庫連線
* @throws NoSuchDBException 不支援的資料庫類型
* @throws SQLException 連線失敗
*/
public static Connection getInstance(String dbType, String url)
throws NoSuchDBException, SQLException {
if ("mysql".equalsIgnoreCase(dbType)) {
return getMySqlConn(url);
}
if ("oracle".equalsIgnoreCase(dbType)) {
return getOracleConn(url);
}
throw new NoSuchDBException("Unsupported database type: " + dbType);
}
/** 安全關閉連線,吞掉例外避免影響主流程 */
public static void closeConn(Connection conn) {
if (conn != null) {
try { conn.close(); }
catch (SQLException e) { e.printStackTrace(); }
}
}
private static Connection getMySqlConn(String url) throws SQLException {
try {
// MySQL 8.0+ 驅動類為 com.mysql.cj.jdbc.Driver
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("MySQL JDBC Driver not found", e);
}
// 建議從配置檔讀取帳密,此處僅示範
return DriverManager.getConnection(MYSQL_PREFIX + url, "root", "root");
}
private static Connection getOracleConn(String url) throws SQLException {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
throw new SQLException("Oracle JDBC Driver not found", e);
}
return DriverManager.getConnection(ORACLE_PREFIX + url, "scott", "tiger");
}
// 自訂例外
public static class NoSuchDBException extends Exception {
public NoSuchDBException(String msg) { super(msg); }
}
}
import java.sql.*;
public class Demo {
public static void main(String[] args) {
String dbType = "mysql"; // 可從配置讀取
String url = "localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = ConnectDB.getInstance(dbType, url);
ps = conn.prepareStatement("SELECT VERSION()");
rs = ps.executeQuery();
if (rs.next()) {
System.out.println("DB Version: " + rs.getString(1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 逆序關閉資源
try { if (rs != null) rs.close(); } catch (SQLException ignored) {}
try { if (ps != null) ps.close(); } catch (SQLException ignored) {}
ConnectDB.closeConn(conn);
}
}
}
| 維度 | 建議 |
|---|---|
| 連線池 | 整合 HikariCP / Druid / DBCP2,勿直接使用 DriverManager |
| 配置外部化 | URL、帳密、驅動類放入 application.yml / properties,支援多環境 |
| 驅動載入 | JDBC 4.0+ (Java 6+) 透過 SPI 自動載入,無需 Class.forName |
| 例外處理 | 自訂例外體系,區分連線失敗、SQL 錯誤、資料完整性錯誤 |
| 多租戶/分庫 | 擴充 key 為 tenantId/dbKey 的連線池映射表 |
| 監控指標 | 整合 Micrometer / Prometheus 監控連線池狀態 |
serverTimezone=UTC 或 Asia/Shanghai,否則報 CJ CommunicationsException。thin:@host:port:SID 或 thin:@host:port/service_name,別混淆。NoSuchMethodError。finally 或 try-with-resources 關閉 Connection/Statement/ResultSet。# application.yml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
minimum-idle: 5
實際專案建議直接使用 Spring DataSource 抽象,而非自寫工廠類。