1. 程式人生 > 其它 >JDBC基礎入門(1)

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;
}