zoukankan      html  css  js  c++  java
  • 自定义数据库连接池实现方式 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 },这样无论数据库驱动是什么版本的驱动,都能保证这个类型转换不出错。

     欢迎关注公众号

  • 相关阅读:
    java第二周的学习知识
    java第一次课
    今天晚上有事,代码假期完成。
    [leetcode] Minimum Path Sum
    java访问权限整理
    java IO整理-File
    java初始化顺序
    enum使用整理
    工厂模式总结
    java IO-流
  • 原文地址:https://www.cnblogs.com/zhaixingzhu/p/12569184.html
Copyright © 2011-2022 走看看