1. 程式人生 > >自定義資料庫連線池實現方式 MySQL

自定義資料庫連線池實現方式 MySQL

應用程式直接獲取資料庫連線缺點

使用者每次請求都會建立一次資料庫連線,並且資料庫建立連線會消耗相對大的資源和時間。
如果針對於個別的工具或者是大量的程式碼測試甚至系統執行,對資料庫操作次數頻繁,極大的佔用資料庫資源,有可能會發生宕機或者記憶體溢位的現象。

而在大多的專案中,常常用到阿里巴巴開源的資料庫連線池框架,準確來說它不僅僅包括資料庫連線池,
原因其實很簡單,在Spring框架的配置檔案中僅僅一個配置datasource就可以使用Druid了
我們對Druid不加探討,實現自定義的資料庫連線池,來對資料進行處理。

在pom檔案中引人資料庫所需要的jar包,這裡不一一介紹。
建立JdbcConnectionsPool類實現資料來源介面DataSource重寫其getConnection方法

package com.herbert.test.db;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
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.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;


/**
 * 實現資料庫連線池
 */
public class JdbcConnectionsPool implements DataSource {
    //

    /*
     * 使用靜態塊程式碼,初始化連線池,建立連線池的中最小連結數量連線,
     * 建立linkedlist集合,將這些連線放入集合中
     */
    //建立linkedlist集合
    private static LinkedList<Connection> linkedlist1 = new LinkedList<Connection>();
    public static String GENERATOR_JDBCDRIVER = "";

    public static String GENERATOR_JDBCURL = "";

    public static String GENERATOR_JDBCUSERNAME = "";

    public static String GENERATOR_JDBCPASSWORD = "";

    private static int jdbcConnectionInitSize;//最小連線數量

    private static int max = 1; //當前最大連線數量=max*jdbcConnectionInitSize

    private static Properties prop = new Properties();

    static {
        //通過反射機制獲取訪問db.properties檔案
        InputStream in = Object.class.getResourceAsStream("/generator.properties");

        try {
            //載入db.properties檔案
            prop.load(in);
            //獲取db.properties檔案中的資料庫連線資訊
            GENERATOR_JDBCDRIVER = prop.getProperty("generator.jdbc.driver").trim();
            GENERATOR_JDBCURL = prop.getProperty("generator.jdbc.url").trim();
            GENERATOR_JDBCUSERNAME = prop.getProperty("generator.jdbc.username").trim();
            GENERATOR_JDBCPASSWORD = prop.getProperty("generator.jdbc.password").trim();
            jdbcConnectionInitSize = Integer.parseInt(prop.getProperty("jdbcConnectionInitSize"));

            Class.forName(GENERATOR_JDBCDRIVER);

            //建立最小連線數個數據庫連線物件以備使用
            for (int i = 0; i < jdbcConnectionInitSize; i++) {
                Connection conn = DriverManager.getConnection(GENERATOR_JDBCURL, GENERATOR_JDBCUSERNAME, GENERATOR_JDBCPASSWORD);
                System.out.println("獲取到了連結" + conn);
                //將建立好的資料庫連線物件新增到Linkedlist集合中
                linkedlist1.add(conn);
            }


        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    @Override
    public Connection getConnection() throws SQLException {
        //如果集合中沒有資料庫連線物件了,且建立的資料庫連線物件沒有達到最大連線數量,可以再建立一組資料庫連線物件以備使用
        if (linkedlist1.size() == 0 && max <= jdbcConnectionInitSize) {
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            for (int i = 0; i < jdbcConnectionInitSize; i++) {
                Connection conn = DriverManager.getConnection(GENERATOR_JDBCURL, GENERATOR_JDBCUSERNAME, GENERATOR_JDBCPASSWORD);
                System.out.println("獲取到了連結" + conn);
                //將建立好的資料庫連線物件新增到Linkedlist集合中
                linkedlist1.add(conn);
            }
            max++;
        }
        if (linkedlist1.size() > 0) {
            //從linkedlist集合中取出一個數據庫連結物件Connection使用
            final Connection conn1 = linkedlist1.removeFirst();
            System.out.println("linkedlist1資料庫連線池大小是" + linkedlist1.size());
                        /*返回一個Connection物件,並且設定Connection物件方法呼叫的限制,
            *當呼叫connection類物件的close()方法時會將Connection物件重新收集放入linkedlist集合中。
            */
            return

          (Connection) Proxy.newProxyInstance(JdbcConnectionsPool.class.getClassLoader(),//這裡換成JdbcConnectionsPool.class.getClassLoader();也行
                  new Class[] { Connection.class }, new InvocationHandler() {

                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if (!method.getName().equalsIgnoreCase("close")) {
                                return method.invoke(conn1, args);
                            } else {
                                linkedlist1.add(conn1);
                                System.out.println(conn1 + "物件被釋放,重新放回linkedlist集合中!");
                                System.out.println("此時Linkedlist集合中有" + linkedlist1.size() + "個數據庫連線物件!");
                                return null;
                            }

                        }
                    });

        } else {
            System.out.println("連線資料庫失敗!");
        }
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

對方法進行進一步封裝

package com.herbert.test.db;

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

/**
 * Created by Herbert on 2018/7/23.
 */
public class JdbcConnectionPoolUtil {
    /**
     * @Field: pool
     * 資料庫連線池
     */
    private static JdbcConnectionsPool pool = new JdbcConnectionsPool();

    /**
     * @return Connection資料庫連線物件
     * @throws SQLException
     * @Method: getConnection
     * @Description: 從資料庫連線池中獲取資料庫連線物件
     */
    public static Connection getConnection() throws SQLException {
        return pool.getConnection();
    }

    /**
     * @param conn
     * @param st
     * @param rs
     * @Method: release
     * @Description: 釋放資源,
     * 釋放的資源包括Connection資料庫連線物件,負責執行SQL命令的Statement物件,儲存查詢結果的ResultSet物件
     */
    public static void release(Connection conn, Statement st, ResultSet rs) {
        if (rs != null) {
            try {
                //關閉儲存查詢結果的ResultSet物件
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                //關閉負責執行SQL命令的Statement物件
                st.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (conn != null) {
            try {
                //關閉Connection資料庫連線物件
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

測試方法

 public static void main(String[] args) throws SQLException {

        JdbcConnectionPoolUtil jcpt = new JdbcConnectionPoolUtil();
        List list = new ArrayList();
        Connection c = null;
        Statement stmt = null;
        try {
            c = jcpt.getConnection();
            c.setAutoCommit(false);
            stmt = c.createStatement();
            String sql = "SELECT id FROM test";
            PreparedStatement preState = c.prepareStatement(sql);

            ResultSet rs = preState.executeQuery();

            while (rs.next()) {
                String id = rs.getString("id");
                list.add(id);
            }
            jcpt.release(c, stmt, rs);
        } catch (Exception e) {
            System.err.println(e.getClass().getName() + ": " + e.getMessage());
            System.exit(0);
        }
        System.out.println("測試:"+list.toString());

    }

輸出結果:

獲取到了連結[email protected]
獲取到了連結[email protected]
獲取到了連結[email protected]
獲取到了連結[email protected]
獲取到了連結[email protected]
獲取到了連結[email protected]
獲取到了連結com.mysql.jdbc.JDBC4Connection[email protected]
獲取到了連結[email protected]
獲取到了連結[email protected]
獲取到了連結[email protected]
linkedlist1資料庫連線池大小是9
[email protected]物件被釋放,重新放回linkedlist集合中!
此時Linkedlist集合中有10個數據庫連線物件!
測試:[1, 2]

異常處理

java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to java.sql.Connection異常問題解決

程式碼如下

Connection proxy = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(),
                Connection.class.getInterfaces(), new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        if ("close".equals(method.getName())) {
                            returnConn(conn);
                            return null;
                        } else {
                            return method.invoke(conn, args);
                        }
                    }
                });

在使用動態代理增強Connection連線物件的close方法時,我碰到了如題所示的異常。通過搜尋我發現這個異常出現的原因在於我使用的mysql資料庫驅動的問題,由於資料庫驅動不同,Connection.class.getInterfaces()返回的結果也不同,它返回的是一個Class[]陣列,然而此陣列的第一個元素必須是Connection才能把建立的代理類轉為Connection物件,否則就會報錯。

  所以這裡我們可以採取一個替代方式替換Connection.class.getInterfaces(),即new Class[] { Connection.class },這樣無論資料庫驅動是什麼版本的驅動,都能保證這個型別轉換不出錯。

 歡迎關注公眾號

相關推薦

定義資料庫連線實現方式 MySQL

應用程式直接獲取資料庫連線缺點 使用者每次請求都會建立一次資料庫連線,並且資料庫建立連線會消耗相對大的資源和時間。 如果針對於個別的工具或者是大量的程式碼測試甚至系統執行,對資料庫操作次數頻繁,極大的佔用資料庫資源,有可能會發生宕機或者記憶體溢位的現象。 而在大多的專案中,常常用到阿里巴

定義資料庫連線實現方式 MySQL

應用程式直接獲取資料庫連線缺點 使用者每次請求都會建立一次資料庫連線,並且資料庫建立連線會消耗相對大的資源和時間。 如果針對於個別的工具或者是大量的程式碼測試甚至系統執行,對資料庫操作次數頻繁,極大的佔用資料庫資源,有可能會發生宕機或者記憶體溢位的現象。 而在大多的專案中

經典的java定義資料庫連線程式碼

import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Driver; import java.sql.DriverManager; import j

(二十)定義資料庫連線

目錄 使用資料庫連線池優化資料庫效能 之前我們對資料庫的操作,其實是有很大問題的; 因為我們是每次操作資料庫之前,都會問資料庫要一個連線,用完之後,就把這個連結還給了資料庫; 其實資料庫連線是重量級的東西,資料庫每次建立一個連接出來,都要花

【Java】Spring和Tomcat帶的連線實現資料庫操作

@[toc] 前言 前面我們已經用Spring和傳統的Jdbc實現資料庫操作、Spring和JdbcTemplate實現資料庫操作。但是這些都是基於直連的資料來源進行的,現在我們將介紹基於連線池的資料來源進行資料庫操作。前面幾個步驟都相同。 建立資料庫 首先建立我們的資料庫(這裡我使用的是Mysql)

servlet+jsp+mysql+資料庫連線實現註冊登陸驗證碼功能

首先專案的結構及所用到的jar包如圖: 主要用到jdbc和jstl的jar包,大家可自行去相應網站下載 一、資料庫和資料表的建立 1.建庫語句: create database test; 2.建表語句: CREATE TABLE `t_users` (  

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

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

【CAS】定義資料庫連線配置

      在上一篇部落格中,對CAS進行了整體的介紹,我們知道,CAS可分為Server和Client。本篇部落格開始介紹對CAS  Sever進行的一些自定義配置。 【版本說明】:CAS Server 4.0 【下載地址】:https://github.com/aper

Spring + MyBatis 資料庫連線加密實現方式

近期因專案需要,客戶要求資料庫使用者名稱和密碼需加密實現連線。專案實現框架SSM,上網查閱一番資料後,發現有部分資料分享的不是很完善,在此寫下隨筆,以便大家採納及提出建議。有寫的不對的地方,歡迎給予指正。 以下來介紹我的實現方式 1、配置 jdbc.properties

java資料庫連線實現原理

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

定義實現資料庫連線

資料庫連線池: >資料庫的連線物件建立工作,比較消耗效能 >一開始先在記憶體中開闢一塊空間(集合) , 先往池子裡面放置 多個連線物件。  後面需要連線的話,直接從池子裡面去。不要去自己建立連線了。  使用完畢, 要記得歸還連線。確保連線物件能迴圈利用。即建立

Druid連線定義資料庫密碼加解密的實現

Druid的功能 1、替換DBCP和C3P0。Druid提供了一個高效、功能強大、可擴充套件性好的資料庫連線池。 2、可以監控資料庫訪問效能,Druid內建提供了一個功能強大的StatFilter外掛,能夠詳細統計SQL的執行效能,這對於線上分析資料庫訪問效

資料庫連線原理詳解與定義連線實現

實現原理資料庫連線池在初始化時將建立一定數量的資料庫連線放到連線池中,這些資料庫連線的數量是由最小資料庫連線數制約。無論這些資料庫連線是否被使用,連線池都將一直保證至少擁有這麼多的連線數量。連線池的最大資料庫連線數量限定了這個連線池能佔有的最大連線數,當應用程式向連線池請求的連

RF定義資料庫訪問模組,連線mysql資料庫

程式碼簡單,不多贅述 import MySQLdb class Mysqlexc(): database_name = 'payment' host = '1.1.1.2' port = '3306' username = 'root' passwor

Javaweb總結2—定義JDBC資料庫連線

什麼是資料庫連線池呢? 資料庫連線池簡而言之就是一個容器裡存放一些資料庫連線。 那問題來了,要資料庫連線池有什麼用呢? 哈哈不用急,接下來我們一起慢慢分析分析 我們仔細觀察這個連線池,有沒有解決剛剛開始的疑問呢? 實現連線池先繼承一個DataSourse類,當然也可以選擇不繼承它來寫

python3.6實現mysql資料庫連線

首先安裝資料連線池模組 pip3 install DBUtils 然後安裝mysql驅動包 pip3 install PyMySQL 安裝完成之後,在專案中settings檔案裡面配置好資料連線資訊,如下圖: 新建一個myql_help檔案,名稱自己取,然後複製參考以下

[C++]MYSQL 資料庫操作封裝及連線實現

Database類為單例類、執行緒安全、實現了連線池,並且封裝所需要的操作。 本程式碼在Ubuntu下測試可用,使用Mysql connector c++連線資料庫,並啟用C++11特性。 基本操作如下: //資料庫配置 DbSetting set

使用連線方式連線資料庫:使用DBUtil連線MYSQL資料庫

==================== DBUtil.java: ==================== package blog.util; import java.sql.Connection; import java.sql.PreparedStatement

定義JDBC資料庫連線小例子

上篇文章中寫了一個JDBC的小例子,這篇文章寫個資料庫連線池的小例子吧。 package com.zkn.newlearn.jdbc.mysql.third; import java.io.IOException; import java.io.InputStream;

Python實現Mysql資料庫連線

python連線Mysql資料庫: python程式設計中可以使用MySQLdb進行資料庫的連線及諸如查詢/插入/更新等操作,但是每次連線mysql資料庫請求時,都是獨立的去請求訪問,相當浪費資源,而且訪問數量達到一定數量時,對mysql的效能會產生較大的影響。因此,實際使