1. 程式人生 > >mysql筆記五——資料庫連線池(原理、構建)和java動態代理的使用

mysql筆記五——資料庫連線池(原理、構建)和java動態代理的使用

資料庫連線池

1、什麼是資料庫連線池?
      資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是再重新建立一個;釋放空閒時間超過最大空閒時間的資料庫連線來避免因為沒有釋放資料庫連線而引起的資料庫連線遺漏。這項技術能明顯提高對資料庫操作的效能。
2、為什麼要引入資料庫連線池?出現的原因
       使用者每次請求都需要向資料庫獲得連結,而資料庫建立連線通常需要消耗相對較大的資源,建立時間也較長。假設網站一天10萬訪問量,資料庫伺服器就需要建立10萬次連線,極大的浪費資料庫的資源,並且極易造成資料庫伺服器記憶體溢位、拓機。在多執行緒的環境下容易浪費資源
       資料庫連線池的出現就是為了減少伺服器的負擔,提高資源的利用效率。
3、原理:資料庫連線池的原理就是回收機制,也就是關閉的連線能及時在利用,避免浪費。

下面是簡單版的資料庫連線池
ConnsUtils.javal類

package cn.hncu.pool;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;



public class ConnsUtils {
    private static
List<Connection> list=new ArrayList<Connection>(); static{ Properties p=new Properties(); try { p.load(ConnsUtils.class.getClassLoader().getResourceAsStream("jdbc.properities")); String url = p.getProperty("url"); String user=p.getProperty("user"
);String driver=p.getProperty("driver"); Class.forName(driver);//如果是tomcat中,載入驅動不能少 String password=p.getProperty("password"); for(int i=0;i<3;i++){ Connection con=DriverManager.getConnection(url, user, password); list.add(con); } } catch (IOException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public static Connection getCon(){ synchronized (list) { if (list.size()<=0) { try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } return list.remove(0); } public static void back(Connection con){ synchronized (list) { System.out.println("還回來了一個連線...."); list.notify(); list.add(con); } } }

測試:PoolTest .java

package cn.hncu.pool;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import cn.hncu.utils.ConnectFactory;

public class PoolTest {
    public static void main(String[] args) {
        Connection con=null;
        try {
            con = ConnsUtils.getCon();
        } catch (Exception e) {
            System.out.println("正在等待資源");
        }
        try {
            con.setAutoCommit(false);
            Statement st = con.createStatement();
            String sql = "insert into book(name) values('bb')";
            st.execute(sql);
            new OneThread(1).start();
            new OneThread(2).start();
            new OneThread(3).start();
            new OneThread(4).start();
            new OneThread(5).start();
            sql = "insert into book(name) values('bb2') ";
            st.execute(sql);
            System.out.println("主執行緒準備提交");
            con.commit();
            System.out.println("主執行緒提交完畢");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("主執行緒回滾了...");
            } catch (SQLException e1) {
                System.out.println("主執行緒回滾失敗...");
            }

        }finally{
            if(con!=null){
                try {
                    con.setAutoCommit(true);
                    ConnsUtils.back(con);//還資源
//                  con.close();
                } catch (SQLException e) {
                    throw new RuntimeException("連線關閉失敗!", e);
                }
            }

        }
    }
}

class OneThread extends Thread{
    private int i;

    public OneThread(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        Connection con=ConnsUtils.getCon();
        try {
            con.setAutoCommit(false);
            Statement st = con.createStatement();
            String sql = "insert into book(name) values('aa="+i+"')";
            st.execute(sql);
            sql = "insert into book(name) values('aaa="+i+"') ";
            st.execute(sql);
            System.out.println("第"+i+"執行緒準備提交");
            con.commit();
            System.out.println("第"+i+"個執行緒提交完畢");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("第"+i+"個執行緒回滾了...");
            } catch (SQLException e1) {
                System.out.println("第"+i+"個執行緒回滾失敗...");
            }

        }finally{
            if(con!=null){
                try {
                    con.setAutoCommit(true);
                    ConnsUtils.back(con);//還資源
//                  con.close();
                } catch (SQLException e) {
                    throw new RuntimeException("連線關閉失敗!", e);
                }
            }

        }
    }

}
    我們發現,con物件不能關閉,否則就會出現,拿不到Connection物件的情況,導致查詢無法完成,所有必須引入動態代理來解決這個弱點。

動態代理

1、簡介:Java動態代理類位於Java.lang.reflect包下,一般主要涉及到以下兩個類:

  • Interface InvocationHandler:該介面中僅定義了一個方法Object:invoke(Object obj,Method method,Object[]
    args)。在實際使用時,第一個引數obj一般是指代理類(要小心使用,不小心就是死迴圈),method是被代理的方法,args為該方法的引數陣列。這個抽象方法在代理類中動態實現。
  • Proxy:該類即為動態代理類,作用類似於上例中的ProxySubject,其中主要包含以下內容: Protected
    Proxy(InvocationHandler h):建構函式,估計用於給內部的h賦值。 Static Class getProxyClass (ClassLoader loader,Class[]
    interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部介面的陣列。
    2、代理機制及特點
  • 通過實現 InvocationHandler 介面建立自己的呼叫處理器; 通過為 Proxy 類指定 ClassLoader 物件和一組
    interface 來建立動態代理類;
  • 通過反射機制獲得動態代理類的建構函式,其唯一引數型別是呼叫處理器介面型別;
  • 通過建構函式建立動態代理類例項,構造時呼叫處理器物件作為引數被傳入

3、核心程式碼:我通過圖片來進行註解
這裡寫圖片描述

下面演示詳細程式碼,與資料庫連線池聯合使用
ConnsUtils.javal類

package cn.hncu.proxy;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class ConnsUtils {
    private static List<Connection> pool=new ArrayList<Connection>();//建立一個pool池物件
    private static final int NUM=3;//池中最大數
    static{
        Properties p=new Properties();//從配置檔案讀取
        try {
//          p.load(ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properities"));
            p.load(ConnsUtils.class.getClassLoader().getResourceAsStream("jdbc.properities"));
            String url = p.getProperty("url");
            String user=p.getProperty("user");
            String password=p.getProperty("password");
            for(int i=0;i<NUM;i++){
                final Connection con=DriverManager.getConnection(url, user, password);//final必須設定,因為內部類訪問
                //使用動態代理,代理conn物件,實現對close方法的攔截
                Object obj=Proxy.newProxyInstance(ConnsUtils.class.getClassLoader(), 
                                                new Class[]{Connection.class},new InvocationHandler() {
                    //proxy是代理後的物件(等價於返回的obj), method就是類反射中的方法物件, args是執行method方法所需的引數
                                                    @Override
                                                    public Object invoke(Object proxy, Method method, Object[] args)
                                                            throws Throwable {

                                                        if(method.getName().equalsIgnoreCase("close")&&(args==null||args.length==0)){
                                                            synchronized (pool) {
                                                                pool.add((Connection) proxy);//需要強轉
                                                                pool.notify();//喚醒等待的資源
                                                            }
                                                            return null;
                                                        }
                                                        return method.invoke(con, args);//其他方法就行放行
                                                    }
                                                });
                pool.add((Connection)obj);//把代理後的conn物件即obj
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static Connection getCon(){
        synchronized (pool) {//同步鎖,一次只能一個進來拿,避免同時拿
            if(pool.size()<=0){
                try {
                    pool.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return pool.remove(0);
        }

    }
}

我們會發現,就算我們在PoolTest下呼叫了con.close()函式,我們也沒有關閉Connection物件,因為我們在ConnsUtils中通過動態代理把con.close()進行了攔截並更改!

相關推薦

mysql筆記——資料庫連線原理構建java動態代理的使用

資料庫連線池 1、什麼是資料庫連線池?       資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是再重新建立一個;釋放空閒時間超過最大空閒時間的資料庫連線來避免因為沒有釋放資料庫連線而引起的資料庫連線遺漏。這項

JDBC 資料庫連線DBCPC3P0 詳解

前言   這段時間狀態有一點浮躁,希望自己靜下心來。還有特別多的東西還沒有學懂。需要學習的東西非常的多,加油! 一、JDBC複習   Java Data Base Connectivity,java資料庫連線,在需要儲存一些資料,或者拿到一些資料的時候,就需要往

JDBC資料庫連線DBCPC3P0

前言   這段時間狀態有一點浮躁,希望自己靜下心來。還有特別多的東西還沒有學懂。需要學習的東西非常的多,加油! 一、JDBC複習   Java Data Base Connectivity,java資料庫連線,在需要儲存一些資料,或者拿到一些資料的時候,就需要往資料庫裡存取資料。那麼java如何連線資料

Java——Web開發之開源的資料庫連線C3P0與DBCP的使用

緊接上一篇資料庫連線池的學習,點連線直達~   資料庫連線池的簡單理解與使用 資料庫連線池DBCP程式碼連線與配置檔案: 1.先匯入使用的jar檔案,分別是dbcp.jar與pool.jar檔案 2.分別使用兩種方式實現,使用配置檔案(dbcpconfig.

常用資料庫連線dbpc,c3p0,Druid

1. 引言 1.1 定義 資料庫連線是一種關鍵的有限的昂貴的資源,這一點在多使用者的網頁應用程式中體現得尤為突出。對資料庫連線的管理能顯著影響到整個應用程式的伸縮性和健壯性,影響到程式的效能指標。資料庫連線池正是針對這個問題提出來的。 資料庫連線池負責分配、管理和釋放資料庫連線,它允許應

使用資料庫連線C3P0管理資料來源出現的Too many connections問題

使用了Spring的HibernateDaoSupport管理資料DAO,如果在方法中使用了this.getSession()獲取session後,不在後面關閉session(session.close()),就會造成

Java個人總結——JDBC資料庫連線

三種常見的資料庫連線池 一、DBCP連線池 DBCP(DataBase connection pool),[資料庫連線池]。是 apache 上的一個 java 連線池專案,也是 tomcat 使用的連線池元件。單獨使用dbcp需要2個包:commons-dbcp.

配置資料庫連線資料來源--------java利用BasicDataSource

DataSource又稱為資料來源,該類的目的是為了防止使用資料庫時不必要操作帶來的資源浪費,使用資料庫時需要進行資料庫連線,才可以使用資料庫,使用資料庫後,又要斷開連結,用來釋放資源。連結資料庫和斷開資料庫連線浪費資源,而且在頻繁操作資料庫的時候,如果按照沒有資料來源的方式

理解資料庫連線底層原理之手寫實現

前言 資料庫連線池的基本思想是:為資料庫連線建立一個“緩衝池”,預先在池中放入一定數量的資料庫連線管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向資料庫申請資源,釋放資源帶來的效能損耗。在如今的分散式系統當中,系統的QPS瓶頸往往就

自制Mysql資料庫連線工具含使用說明

我們在對mysql資料庫進行操作時,就要使用JDBC去連線資料庫,所以程式碼不免要出現大量的冗餘,比如連線,關閉等等實現其實都是一樣的,所以聰明的程式設計師就會將這些重複的功能封裝,簡化使用過程,提高程式碼複用性。 1.BaseDao原始碼 package com.x

Spring Boot 整合 Mybatisdruid 資料庫連線 以及 分頁配置

MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映,目前很大一部分網際網路、軟體公司都在使用這套框架 下來來之後,我們主要關注幾個檔案 配置檔案:generator.properties 主要是資料庫的相關配置,以及檔案生成的根路徑 generator.jdbc.drive

資料庫連線原理機制

       1、基本概念及原理   對於共享資源,有一個很著名的設計模式:資源池(Resource  Pool)。該模式正是為了解決資源的頻繁分配?釋放所造成的問題。為解決上述問題,可以採用資料庫連線池技術。資料庫連線池的基本思想就是為資料庫連線建立一個“緩衝池”。預先在緩衝池中放入一定數量的連線,當需要建

資料庫連線底層原理以及手寫實現

資料庫連線池的基本思想是:為資料庫連線建立一個“緩衝池”,預先在池中放入一定數量的資料庫連線管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向資料庫申請資源,釋放資源帶來的效能損耗。 在如今的分散式系統當中,系統的QPS瓶頸往往就

資料庫連線原理。為什麼要使用連線

1,資料庫連線是一件費時的操作,連線池可以使多個操作共享一個連線。 2,資料庫連線池的基本思想就是為資料庫連線建立一個“緩衝池”。預先在緩衝池中放入一定數量的連線,當需要建立資料庫連線時,只需從“緩衝

資料庫連線原理與實現(詳解)

資料庫連線池類的設計 類1:資料庫連線池類DBconnctionPool 屬性: inuserd 當前正在使用的連線數(int 型別) maxConn 最大的連線數(int 型別) minConn 最小的連線數(int 型別) poolName 連線池的名稱(String 型別) ArrayList free

JDBC資料庫連線實現原理(手動實現)

一、普通的資料庫連線     如下圖所示,個使用者獲取資料庫資料都要單獨建立一個jdbc連線,當用戶獲取資料完成後再將連線釋放,可見對cpu的資源消耗很大。  二

JDBC4----------資料庫連線dbcp連線

1、JDBC:是提供用來執行SQL語句的java API.步驟如下:載入驅動程式、與資料庫建立連線、傳送SQL語句、處理結果。2、JTA:事務有提交和回滾兩個概念。提交:所有的操作步驟都被完整的執行後,稱為事務被提交。回滾:由於某一操作步驟執行失敗,導致所有步驟都沒有被提交,

淺析資料庫連線

由於最近專案要用到資料庫連線池,所以今天簡單的聊聊~, 這個話題的引起是因為我在寫mysql資料庫部分時產生了一個疑問,一般後臺處理資料部分,服務端是建立一個connection連線到資料庫,然後所有的請求通過這一個connection來訪問資料庫,

JDBC程式設計學習筆記資料庫連線的實現

在JDBC程式設計的時候,獲取到一個數據庫連線資源是很寶貴的,倘若資料庫訪問量超大,而資料庫連線資源又沒能得到及時的釋放,就會導致系統的崩潰甚至宕機。造成的損失將會是巨大的。再看有了資料庫連線池的JDBC,就會較好的解決資源的建立與連線問題,其主要還是針對於連線

java資料庫連線實現原理

原文:http://blog.csdn.net/frightingforambition/article/details/25464129  一、為什麼在連線資料庫時要使用連線池  資料庫連線是一種關鍵的有限的昂貴的資源,這一點在多使用者的網頁應用程式中體現得