1. 程式人生 > >【JDBC詳解】連線池與分頁

【JDBC詳解】連線池與分頁

一、連線池

1.由來:

一個使用者至少要用到一個連線。當用戶過多時,需要建立巨大數量的連線物件,這會使資料庫承受極大的壓力,為了解決這種現象,出現了資料庫連線池。

2.定義:

在使用者和資料庫之間建立一個”池”,這個池中有若干個連線物件,當用戶想要連線資料庫,就要先從連線池中獲取連線物件,然後操作資料庫。即資料庫連線池就是提供連線的

這裡寫圖片描述



3.自定義連線池

例:

public class MyPool {
    private int init_count = 3;     // 初始化連線數目
    private int max_count = 6;      // 最大連線數
private int current_count = 0; // 記錄當前使用連線數 // 連線池 (存放所有的初始化連線) private LinkedList<Connection> pool = new LinkedList<Connection>(); //1. 建構函式中,初始化連線放入連線池 public MyPool() { // 初始化連線 for (int i=0; i<init_count; i++){ // 記錄當前連線數目 current_count++; // 建立原始的連線物件
Connection con = createConnection(); // 把連線加入連線池 pool.addLast(con); } } //2. 建立一個新的連線的方法 private Connection createConnection(){ try { Class.forName("com.mysql.jdbc.Driver"); // 原始的目標物件 final Connection con = DriverManager.getConnection("jdbc:mysql:///test"
, "root", "965847"); // 對con建立其代理物件 Connection proxy = (Connection) Proxy.newProxyInstance( con.getClass().getClassLoader(), // 類載入器 //con.getClass().getInterfaces(), // 當目標物件是一個具體的類的時候 new Class[]{Connection.class}, // 目標物件實現的介面 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 方法返回值 Object result = null; // 當前執行的方法的方法名 String methodName = method.getName(); // 判斷當執行了close方法的時候,把連線放入連線池 if ("close".equals(methodName)) { System.out.println("begin:當前執行close方法開始!"); // 連線放入連線池 pool.addLast(con); System.out.println("end: 當前連線已經放入連線池了!"); } else { // 呼叫目標物件方法 result = method.invoke(con, args); } return result; } } ); return proxy; } catch (Exception e) { throw new RuntimeException(e); } } //3. 獲取連線 public Connection getConnection(){ // 3.1 判斷連線池中是否有連線, 如果有連線,就直接從連線池取出 if (pool.size() > 0){ return pool.removeFirst(); } // 3.2 連線池中沒有連線: 判斷,如果沒有達到最大連線數,建立; if (current_count < max_count) { // 記錄當前使用的連線數 current_count++; // 建立連線 return createConnection(); } // 3.3 如果當前已經達到最大連線數,丟擲異常 throw new RuntimeException("當前連線已經達到最大連線數目 !"); } //4. 釋放連線 public void realeaseConnection(Connection con) { // 4.1 判斷: 池的數目如果小於初始化連線,就放入池中 if (pool.size() < init_count){ pool.addLast(con); } else { try { // 4.2 關閉 current_count--; con.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } public static void main(String[] args) throws SQLException { MyPool pool = new MyPool(); System.out.println("當前連線: " + pool.current_count); // 使用連線 pool.getConnection(); pool.getConnection(); Connection con3 = pool.getConnection(); Connection con2 = pool.getConnection(); Connection con1 = pool.getConnection(); // 釋放連線, 連線放回連線池 con1.close(); // 再獲取 pool.getConnection(); System.out.println("連線池:" + pool.pool.size()); System.out.println("當前連線: " + pool.current_count); } }



4.DBCP連線池

(1)引入jar包:

• Commons-dbcp.jar:連線池的實現
• Commons-pool.jar:連線池實現的依賴庫

(2)核心類:BasicDataSource

例:

public class Dbcp {
    // 1. 硬編碼方式實現連線池
    @Test
    public void testDbcp() throws Exception {
        // DBCP連線池核心類
        BasicDataSource dataSouce = new BasicDataSource();
        // 連線池引數配置:初始化連線數、最大連線數 / 連線字串、驅動、使用者、密碼
        dataSouce.setUrl("jdbc:mysql:///test");//資料庫連線字串
        dataSouce.setDriverClassName("com.mysql.jdbc.Driver");//資料庫驅動
        dataSouce.setUsername("root");     //資料庫連線使用者
        dataSouce.setPassword("965847");   //資料庫連線密碼
        dataSouce.setInitialSize(3);       // 初始化連線
        dataSouce.setMaxActive(6);         // 最大連線
        dataSouce.setMaxIdle(3000);        // 最大空閒時間

        // 獲取連線
        Connection con = dataSouce.getConnection();
        con.prepareStatement("delete from admin where id=5").executeUpdate();
        // 關閉
        con.close();
    }

    @Test
    // 2. 【推薦】配置方式實現連線池
    public void testProp() throws Exception {
        // 載入prop配置檔案
        Properties prop = new Properties();
        // 獲取檔案流
        InputStream inStream = Dbcp.class.getResourceAsStream("db.properties");
        // 載入屬性配置檔案
        prop.load(inStream);
        // 根據prop配置,直接建立資料來源物件
        DataSource dataSouce = BasicDataSourceFactory.createDataSource(prop);

        // 獲取連線
        Connection con = dataSouce.getConnection();
        con.prepareStatement("delete from admin where id=4").executeUpdate();
        // 關閉
        con.close();
    }

}



5.C3P0連線池

(1)優點:最常用的連線池技術!Spring框架,預設支援C3P0連線池技術!

(2)核心類:CombopooledDataSource

例:

public class C3p0 {
    @Test
    //1. 硬編碼方式,使用C3P0連線池管理連線
    public void testCode() throws Exception {
        // 建立連線池核心工具類
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        // 設定連線引數:url、驅動、使用者密碼、初始連線數、最大連線數
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc_demo");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setInitialPoolSize(3);
        dataSource.setMaxPoolSize(6);
        dataSource.setMaxIdleTime(1000);

        // ---> 從連線池物件中,獲取連線物件
        Connection con = dataSource.getConnection();
        // 執行更新
        con.prepareStatement("delete from admin where id=7").executeUpdate();
        // 關閉
        con.close();
    }

    @Test
    //2. XML配置方式,使用C3P0連線池管理連線
    public void testXML() throws Exception {
        // 建立c3p0連線池核心工具類
        // 自動載入src下c3p0的配置檔案【c3p0-config.xml】
        ComboPooledDataSource dataSource = new ComboPooledDataSource();// 使用預設的配置

        // 獲取連線
        Connection con = dataSource.getConnection();
        // 執行更新
        con.prepareStatement("delete from admin where id=4").executeUpdate();
        // 關閉
        con.close();

    }

}

二、分頁

1.優點:

利於頁面佈局,且顯示的效率高!

2.要點:

關鍵點:
(1) 分頁SQL語句;
(2) 後臺處理: dao/service/servlet/JSP

3.一個初步的例子

例:

(1)建立分頁的屬性及方法

public class PageBean<T> {
    private int currentPage = 1; // 當前頁, 預設顯示第一頁
    private int pageCount = 4;   // 每頁顯示的行數(查詢返回的行數), 預設每頁顯示4行
    private int totalCount;      // 總記錄數
    private int totalPage;       // 總頁數 = 總記錄數 / 每頁顯示的行數  (+ 1)
    private List<T> pageData;       // 分頁查詢到的資料

    // 返回總頁數
    public int getTotalPage() {
        if (totalCount % pageCount == 0) {
            totalPage = totalCount / pageCount;
        } else {
            totalPage = totalCount / pageCount + 1;
        }
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public int getCurrentPage() {
        return currentPage;
    }
    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }
    public int getPageCount() {
        return pageCount;
    }
    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }
    public int getTotalCount() {
        return totalCount;
    }
    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public List<T> getPageData() {
        return pageData;
    }
    public void setPageData(List<T> pageData) {
        this.pageData = pageData;
    }
}

(2)具體的分頁操作

public class EmployeeDao {

    public void getAll(PageBean<Employee> pb) {

        //2. 查詢總記錄數;  設定到pb物件中
        int totalCount = this.getTotalCount();
        pb.setTotalCount(totalCount);

        //若當前頁 <= 0;         當前頁設定當前頁為1;
        //若當前頁 > 最大頁數;  當前頁設定為最大頁數;
        if (pb.getCurrentPage() <=0) {
            pb.setCurrentPage(1);                       // 把當前頁設定為1
        } else if (pb.getCurrentPage() > pb.getTotalPage()){
            pb.setCurrentPage(pb.getTotalPage());   // 把當前頁設定為最大頁數
        }

        //1. 獲取當前頁: 計算查詢的起始行、返回的行數
        int currentPage = pb.getCurrentPage();
        int index = (currentPage -1 ) * pb.getPageCount();  // 查詢的起始行
        int count = pb.getPageCount();                      // 查詢返回的行數


        //3. 分頁查詢資料;  把查詢到的資料設定到pb物件中
        String sql = "select * from employee limit ?,?";

        try {
            // 得到Queryrunner物件
            QueryRunner qr = JdbcUtils.getQueryRuner();
            // 根據當前頁,查詢當前頁資料(一頁資料)
            List<Employee> pageData = qr.query(sql, new BeanListHandler<Employee>(Employee.class), index, count);
            // 設定到pb物件中
            pb.setPageData(pageData);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public int getTotalCount() {
        String sql = "select count(*) from employee";
        try {
            // 建立QueryRunner物件
            QueryRunner qr = JdbcUtils.getQueryRuner();
            // 執行查詢, 返回結果的第一行的第一列
            Long count = qr.query(sql, new ScalarHandler<Long>());
            return count.intValue();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}





本人才疏學淺,若有錯誤,請指出
謝謝!