zoukankan      html  css  js  c++  java
  • JDBC数据源连接池(4)---自定义数据源连接池

    [续上文《JDBC数据源连接池(3)---Tomcat集成DBCP》]

    我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原理,我在这里写一个自定义的数据源连接池。

    我先在com.itszt.utils包下新建一个Utils_5_mydscp 文件夹,在该文件夹下写自定义的数据源连接池工具;同时,我在src包下建config_utils_5文件夹,该文件夹下建一个属性配置文件mydscp.properties,该属性配置文件信息如下:

    DRIVER_NAME=com.mysql.jdbc.Driver
    JDBC_URL=jdbc:mysql://localhost:3306/itszt2
    USERNAME=root
    PASSWORD=2017
    INITIAL_SIZE=10
    MAX_ACTIVE=100
    MAX_IDLE=20
    MIN_IDLE=10
    

    接着,分析该数据源连接池,需要一个解析属性配置文件的类,需要一个数据源(拟采用单例模式)类,还需要一个Connection类;为了降低耦合度,采用工厂模式获得数据源实例;考虑到异常的个性化处理,还可以自定义异常。

    有了上述分析后,第一步,写一个解析属性配置文件的工具类MyDSCPConfig,代码如下:

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    
    public abstract class MyDSCPConfig {
        public static String DRIVER_NAME = null;
        public static String JDBC_URL = null;
        public static String USERNAME = null;
        public static String PASSWORD = null;
        public static Integer INITIAL_SIZE = null;
        public static Integer MAX_ACTIVE = null;
        public static Integer MAX_IDLE = null;
        public static Integer MIN_IDLE = null;
    
        /**
         * 解析属性配置文件
         *
         * @param propertiesPath
         */
        public static void parseConfig(String propertiesPath) {
            Properties properties = new Properties();
            InputStream resourceAsStream = MyDSCPConfig.class.getClassLoader().getResourceAsStream(propertiesPath);
            try {
                properties.load(resourceAsStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
            DRIVER_NAME = properties.getProperty("DRIVER_NAME");
            JDBC_URL = properties.getProperty("JDBC_URL");
            USERNAME = properties.getProperty("USERNAME");
            PASSWORD = properties.getProperty("PASSWORD");
            INITIAL_SIZE = Integer.parseInt(properties.getProperty("INITIAL_SIZE"));
            MAX_ACTIVE = Integer.parseInt(properties.getProperty("MAX_ACTIVE"));
            MAX_IDLE = Integer.parseInt(properties.getProperty("MAX_IDLE"));
            MIN_IDLE = Integer.parseInt(properties.getProperty("MIN_IDLE"));
        }
    }  

    第二步,先采用适配器模式,写一个实现了Connection 接口的MyConnectionAdapter抽象类,便于MyConnectionAdapter的子类有选择地覆写所需的方法,MyConnectionAdapter类代码如下:

    import java.sql.Array;
    import java.sql.Blob;
    import java.sql.CallableStatement;
    import java.sql.Clob;
    import java.sql.Connection;
    import java.sql.DatabaseMetaData;
    import java.sql.NClob;
    import java.sql.PreparedStatement;
    import java.sql.SQLClientInfoException;
    import java.sql.SQLException;
    import java.sql.SQLWarning;
    import java.sql.SQLXML;
    import java.sql.Savepoint;
    import java.sql.Statement;
    import java.sql.Struct;
    import java.util.Map;
    import java.util.Properties;
    import java.util.concurrent.Executor;
    
    /**
     * 适配器
     */
    public abstract class MyConnectionAdapter implements Connection {
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    
        @Override
        public Statement createStatement() throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return null;
        }
    
        @Override
        public CallableStatement prepareCall(String sql) throws SQLException {
            return null;
        }
    
        @Override
        public String nativeSQL(String sql) throws SQLException {
            return null;
        }
    
        @Override
        public void setAutoCommit(boolean autoCommit) throws SQLException {
        }
    
        @Override
        public boolean getAutoCommit() throws SQLException {
            return false;
        }
    
        @Override
        public void commit() throws SQLException {
        }
    
        @Override
        public void rollback() throws SQLException {
        }
    
        @Override
        public void close() throws SQLException {
        }
    
        @Override
        public boolean isClosed() throws SQLException {
            return false;
        }
    
        @Override
        public DatabaseMetaData getMetaData() throws SQLException {
            return null;
        }
    
        @Override
        public void setReadOnly(boolean readOnly) throws SQLException {
        }
    
        @Override
        public boolean isReadOnly() throws SQLException {
            return false;
        }
    
        @Override
        public void setCatalog(String catalog) throws SQLException {
        }
    
        @Override
        public String getCatalog() throws SQLException {
            return null;
        }
    
        @Override
        public void setTransactionIsolation(int level) throws SQLException {
        }
    
        @Override
        public int getTransactionIsolation() throws SQLException {
            return 0;
        }
    
        @Override
        public SQLWarning getWarnings() throws SQLException {
            return null;
        }
    
        @Override
        public void clearWarnings() throws SQLException {
        }
    
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
                throws SQLException {
            return null;
        }
    
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
            return null;
        }
    
        @Override
        public Map<String, Class<?>> getTypeMap() throws SQLException {
            return null;
        }
    
        @Override
        public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        }
    
        @Override
        public void setHoldability(int holdability) throws SQLException {
        }
    
        @Override
        public int getHoldability() throws SQLException {
            return 0;
        }
    
        @Override
        public Savepoint setSavepoint() throws SQLException {
            return null;
        }
    
        @Override
        public Savepoint setSavepoint(String name) throws SQLException {
            return null;
        }
    
        @Override
        public void rollback(Savepoint savepoint) throws SQLException {
        }
    
        @Override
        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        }
    
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
                throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
                                                  int resultSetHoldability) throws SQLException {
            return null;
        }
    
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
                                             int resultSetHoldability) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
            return null;
        }
    
        @Override
        public Clob createClob() throws SQLException {
            return null;
        }
    
        @Override
        public Blob createBlob() throws SQLException {
            return null;
        }
    
        @Override
        public NClob createNClob() throws SQLException {
            return null;
        }
    
        @Override
        public SQLXML createSQLXML() throws SQLException {
            return null;
        }
    
        @Override
        public boolean isValid(int timeout) throws SQLException {
            return false;
        }
    
        @Override
        public void setClientInfo(String name, String value) throws SQLClientInfoException {
        }
    
        @Override
        public void setClientInfo(Properties properties) throws SQLClientInfoException {
        }
    
        @Override
        public String getClientInfo(String name) throws SQLException {
            return null;
        }
    
        @Override
        public Properties getClientInfo() throws SQLException {
            return null;
        }
    
        @Override
        public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
            return null;
        }
    
        @Override
        public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
            return null;
        }
    
        @Override
        public void setSchema(String schema) throws SQLException {
        }
    
        @Override
        public String getSchema() throws SQLException {
            return null;
        }
    
        @Override
        public void abort(Executor executor) throws SQLException {
        }
    
        @Override
        public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        }
    
        @Override
        public int getNetworkTimeout() throws SQLException {
            return 0;
        }
    }  

    第三步,再写一个继承了MyConnectionAdapter的子类MyConnection,代码如下:

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * 采用装饰者模式,基于Conneciton原有的close()功能,扩展其close()功能
     */
    public class MyConnection extends MyConnectionAdapter {
        private Connection connection;
        private MyDataSource dataSource;
    
        public MyConnection(Connection connection, MyDataSource dataSource) {
            this.connection = connection;
            this.dataSource = dataSource;
        }
    
        @Override
        public Statement createStatement() throws SQLException {
            return connection.createStatement();
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return connection.prepareStatement(sql);
        }
    
        @Override
        public void close() throws SQLException {
            dataSource.recycle(this);
        }
    
        /*
        * 关闭连接,释放资源
        */
        public void dispose() {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }  

    第四步,写一个继承了SQLException的自定义异常MyConnectionExp,代码如下:

    import java.sql.SQLException;
    public class MyConnectionExp extends SQLException {
        public MyConnectionExp(String reason) {
            super(reason);
        }
    }  

    第五步,写一个实现了DataSource接口的抽象类MyDataSourceAdapter,覆写接口的所有方法,该类代码如下:

    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.logging.Logger;
    import javax.sql.DataSource;
    /**
     * 适配器
     */
    public abstract class MyDataSourceAdapter implements DataSource {
        @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;
        }
    
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            return null;
        }
    
        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            return null;
        }
    }  

    第六步,写一个继承了MyDataSourceAdapter 抽象类的子类MyDataSource,由于数据源连接池只能有一个,故采用单例模式,该子类代码如下:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.LinkedList;
    /**
     * 单例模式
     */
    public class MyDataSource extends MyDataSourceAdapter {
        private static MyDataSource myDataSource;
        private LinkedList<MyConnection> connectionQueue = new LinkedList<>();
        private int totalActive = MyDSCPConfig.MAX_ACTIVE;
    
        static {
    // 加载数据库驱动
            try {
                Class.forName(MyDSCPConfig.DRIVER_NAME);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        private MyDataSource() {
        }
    
        //懒汉模式,同步,确保线程安全
        public static MyDataSource getInstance() {
            if (myDataSource == null) {
                synchronized (MyDataSource.class) {
                    if (myDataSource == null) {
                        myDataSource = new MyDataSource();
                        myDataSource.initialize();
                    }
                }
            }
            return myDataSource;
        }
    
        //初始化方法
        public void initialize() {
            for (int i = 0; i < MyDSCPConfig.INITIAL_SIZE; i++) {
                MyConnection connection = produceConnection();
                if (connection == null) {
                    continue;
                }
                connectionQueue.add(connection);
            }
        }
    
        //构建一个连接的方法
        private MyConnection produceConnection() {
            try {
                Connection connection = DriverManager.getConnection(MyDSCPConfig.JDBC_URL, MyDSCPConfig.USERNAME, MyDSCPConfig.PASSWORD);
                return new MyConnection(connection, this);
            } catch (SQLException e) {
                MyConnectionExp exp = new MyConnectionExp("获取连接失败!");
                exp.printStackTrace();
            }
            return null;
        }
    
        //从连接池中获取连接
        @Override
        public Connection getConnection() throws SQLException {
            int num1 = connectionQueue.size();
            MyConnection myConnection = null;
            if (this.totalActive > 0) {
                if (num1 >= 1) {
                    myConnection = connectionQueue.pop();
                    this.totalActive--;
                } else {
                    System.err.println("连接池已无空闲连接,需生产后才能使用!");
                    myConnection = produceConnection();
                    connectionQueue.add(myConnection);
                    getConnection();
                }
                int num2 = connectionQueue.size();
                System.out.println("获取连接,原先有 " + num1 + " 个,现在有 " + num2 + " 个");
                if (num2 < MyDSCPConfig.MIN_IDLE) {
                    System.out.println("不足最小空闲连接数,生产一个新的!");
                    connectionQueue.add(produceConnection());
                }
            } else {
                System.err.println("已有一次超出最大连接数!");
                this.totalActive = MyDSCPConfig.MAX_ACTIVE;
                getConnection();
            }
            return myConnection;
        }
    
        /*
        * 回收连接
        */
        protected void recycle(MyConnection myConnection) {
            if (connectionQueue.size() >= MyDSCPConfig.MAX_IDLE) {
                System.out.println("超出空闲连接数上限,不再回收!");
                myConnection.dispose();
                return;
            } else {
                connectionQueue.add(myConnection);
                this.totalActive++;
            }
        }
    }  

    第七步,基于工厂模式,写一个获取数据源连接池实例的类MyDataSourceFactory,该类代码如下:

    /**
     * 工厂模式
     */
    public abstract class MyDataSourceFactory {
        public static MyDataSource createDataSource(String propertiesPath) {
    //1.解析配置文件中的配置
            MyDSCPConfig.parseConfig(propertiesPath);
    //2.依据上一步骤的设置,生产我们的数据源
            return MyDataSource.getInstance();
        }
    }  

    最后,在com.itszt.demo文件夹中的LoginServlet.java的Servlet文件中,将产生Connection对象的方式修改为自定义的数据源连接池方式即可。

    总结:在Web项目中,我们经常需要频繁连接数据库,采用数据源连接池的方式,能够有效地解决建立数据库连接时耗费较多CPU、时间等资源的问题,从而提高应用性能,改进用户体验。

  • 相关阅读:
    java后端
    2017-12-11
    二叉树与分治法整理
    javaweb
    安装docker
    爬虫
    lintcode
    DEEPlearning
    剑指offer_by牛客网
    DFS
  • 原文地址:https://www.cnblogs.com/lizhangyong/p/8117512.html
Copyright © 2011-2022 走看看