自定義數據庫連接池實現方式 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());

    }

 

輸出結果:

獲取到了鏈接com.mysql.jdbc.JDBC4Connection@b1a58a3
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@46ee7fe8
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@722c41f4
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@2957fcb0
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@f2a0b8e
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@64a294a6
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@61e717c2
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@504bae78
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@5fcfe4b2
獲取到了鏈接com.mysql.jdbc.JDBC4Connection@3f8f9dd6
linkedlist1數據庫連接池大小是9
com.mysql.jdbc.JDBC4Connection@b1a58a3對象被釋放,重新放回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 },這樣無論數據庫驅動是什么版本的驅動,都能保證這個類型轉換不出錯。

 

 歡迎關注公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM