JDBC基礎入門(1)
JDBC(Java Database Connectivity)代表Java程式語言與資料庫連線的標準API,然而JDBC只是介面,JDBC驅動才是真正的介面實現,沒有驅動無法完成資料庫連線. 每個資料庫廠商都有自己的驅動,用來連線自己公司的資料庫(如Oricle, MySQL, DB2, MS SQLServer).
下面我們以MySQL為例,JDBC程式設計大致步驟如下:
/** * @author jifang * @since 16/2/18 上午9:02. */ public class SQLClient { public static void main(String[] args) throws ClassNotFoundException, SQLException { /* 載入資料庫驅動 */ Class.forName("com.mysql.jdbc.Driver"); /* 通過 DriverManager 獲取資料庫連線 */ Connection connection = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password"); /* 通過 Connection 建立 Statement */ Statement statement = connection.createStatement(); /* 通過 Statement 執行SQL */ ResultSet users = statement.executeQuery("SELECT * FROM user"); /* 操作 ResultSet 結果集 */ int columnCount = users.getMetaData().getColumnCount(); while (users.next()) { for (int i = 1; i <= columnCount; ++i) { System.out.printf("%st", users.getObject(i)); } System.out.println(); } /* 回收資料庫資源(推薦使用Java1.7提供的 可以自動關閉資源的try) */ users.close(); statement.close(); connection.close(); } }
注意: 需要在pom.xml中新增如下MySQL驅動:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.36</version>
</dependency>
注: ResultSet引數columnIndex索引從1開始,而不是0!
ConnectionManger
DriverManger
JDBC規定: 驅動類在被載入時,需要主動把自己註冊到DriverManger中:
com.mysql.jdbc.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver { // // Register ourselves with the DriverManager // static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } /** * Construct a new driver and register it with DriverManager * * @throws SQLException * if a database error occurs. */ public Driver() throws SQLException { // Required for Class.forName().newInstance() } }
程式碼顯示:只要去載入com.mysql.jdbc.Driver類那麼就會執行static塊, 從而把com.mysql.jdbc.Driver註冊到DriverManager中.
java.sql.DriverManager是用於管理JDBC驅動的服務類,其主要功能是獲取Connection物件:
1. static Connection getConnection(String url, Properties info)
2. static Connection getConnection(String url, String user, String password)
另: 還可以在獲取Connection的URL中設定引數,如: jdbc:mysql://host:port/database?useUnicode=true&characterEncoding=UTF8
useUnicode=true&characterEncoding=UTF8指定連線資料庫的過程中使用Unicode字符集/UTF-8編碼;
Connection
java.sql.Connection代表資料庫連線,每個Connection代表一個物理連線會話, 該介面提供如下建立Statement的方法, 只有獲取Statement之後才可執行SQL語句:
Creates a Statement object for sending SQL statements to the database.
其中Connection還提供瞭如下控制事務/儲存點的方法:
Creates a savepoint with the given name in the current transaction and returns the new Savepoint object that represents it.
以上方法還存在不同的過載形式, 詳細可參考JDK文件.
ConnectionManger
由於獲取Connection的步驟單一,每次可能只是載入的引數不同,因此我們可以將獲取Connection的操作封裝成一個方法,並使其從配置檔案中載入配置:
配置檔案形式
## Data Source
mysql.driver.class=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://host:port/database
mysql.user=admin
mysql.password=admin
ConnectionManger
/**
* @author jifang
* @since 16/2/19 上午10:40.
*/
public class ConnectionManger {
/*獲取原生Connection*/
public static Connection getConnection(String file) {
Properties config = SQLUtil.loadConfig(file);
try {
Class.forName(config.getProperty("mysql.driver.class"));
String url = config.getProperty("mysql.url");
String username = config.getProperty("mysql.user");
String password = config.getProperty("mysql.password");
return DriverManager.getConnection(url, username, password);
} catch (SQLException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
SQLUtil
/**
* @author jifang
* @since 16/2/18 上午8:24.
*/
public class SQLUtil {
/**
* 載入.properties配置檔案
*
* @param file
* @return
*/
public static Properties loadConfig(String file) {
Properties properties = new Properties();
try {
properties.load(ClassLoader.getSystemResourceAsStream(file));
return properties;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
資料庫連線池
前面通過DriverManger獲得Connection, 一個Connection對應一個實際的物理連線,每次操作都需要開啟物理連線, 使用完後立即關閉;這樣頻繁的開啟/關閉連線會造成不必要的資料庫系統性能消耗.
資料庫連線池提供的解決方案是:當應用啟動時,主動建立足夠的資料庫連線,並將這些連線組織成連線池,每次請求連線時,無須重新開啟連線,而是從池中取出已有連線,使用完後並不實際關閉連線,而是歸還給池.
JDBC資料庫連線池使用javax.sql.DataSource表示, DataSource只是一個介面, 其實現通常由伺服器提供商(如WebLogic, WebShere)或開源組織(如DBCP,C3P0和HikariCP)提供.
資料庫連線池的常用引數如下:
資料庫初始連線數;
連線池最大連線數;
連線池最小連線數;
連線池每次增加的容量;
C3P0
Tomcat預設使用的是DBCP連線池,但相比之下,C3P0則比DBCP更勝一籌(hibernate推薦使用C3P0),C3P0不僅可以自動清理不再使用的Connection, 還可以自動清理Statement/ResultSet, 使用C3P0需要在pom.xml中新增如下依賴:
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.11</version>
</dependency>
ConnectionManger
public class ConnectionManger {
/*雙重檢測鎖保證DataSource單例*/
private static DataSource dataSource;
/*獲取DataSource*/
public static DataSource getDataSourceC3P0(String file) {
if (dataSource == null) {
synchronized (ConnectionManger.class) {
if (dataSource == null) {
Properties config = SQLUtil.loadConfig(file);
try {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setDriverClass(config.getProperty("mysql.driver.class"));
source.setJdbcUrl(config.getProperty("mysql.url"));
source.setUser(config.getProperty("mysql.user"));
source.setPassword(config.getProperty("mysql.password"));
// 設定連線池最大連線數
source.setMaxPoolSize(Integer.valueOf(config.getProperty("pool.max.size")));
// 設定連線池最小連線數
source.setMinPoolSize(Integer.valueOf(config.getProperty("pool.min.size")));
// 設定連線池初始連線數
source.setInitialPoolSize(Integer.valueOf(config.getProperty("pool.init.size")));
// 設定連線每次增量
source.setAcquireIncrement(Integer.valueOf(config.getProperty("pool.acquire.increment")));
// 設定連線池的快取Statement的最大數
source.setMaxStatements(Integer.valueOf(config.getProperty("pool.max.statements")));
// 設定最大空閒時間
source.setMaxIdleTime(Integer.valueOf(config.getProperty("pool.max.idle_time")));
dataSource = source;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
}
}
return dataSource;
}
/*獲取Connection*/
public static Connection getConnectionC3P0(String file) {
return getConnection(getDataSourceC3P0(file));
}
public static Connection getConnection(DataSource dataSource) {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// ...
}
C3P0還可以使用配置檔案來初始化連線池(配置檔案可以是properties/XML, 在此僅介紹XML),C3P0配置檔名必須為c3p0-config.xml,其放在類路徑下:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="jdbcUrl">jdbc:mysql://host:port/database</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">user</property>
<property name="password">password</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">3</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="mysql-config">
<property name="jdbcUrl">jdbc:mysql://host:port/common</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">user</property>
<property name="password">password</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">3</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
這樣, 我們在建立ComboPooledDataSource時就預設載入配置檔案中的配置, 無須手動配置:
public static DataSource getDataSourceC3P0(String file) {
if (dataSource == null) {
synchronized (ConnectionManger.class) {
if (dataSource == null) {
dataSource = new ComboPooledDataSource();
}
}
}
return dataSource;
}
C3P0配置檔案可以配置多個連線資訊, 併為每個配置命名, 這樣可以方便的通過配置名稱來切換配置資訊:
public static DataSource getDataSourceC3P0(String file) {
if (dataSource == null) {
synchronized (ConnectionManger.class) {
if (dataSource == null) {
dataSource = new ComboPooledDataSource("mysql-config");
}
}
}
return dataSource;
}