WEB_10【JDBC连接池】

特此说明:本文参考黑马程序员视频讲座 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

JDBC连接池

上节课学习的JDBC封装工具类,及通过配置文件方便维护,但有个问题是,每次访问数据库都要获取连接并释放资源,这是非常消耗系统资源的。为了解决此类性能问题,我们使用连接池的技术,每次获取连接完成查询后,不是销毁连接,而是放回维护的一个连接池中,这样共享连接。java为数据库连接池提供了公共接口:javax.sql.DataSource(特别强调,注意包名,目前只有Driver在com.mysql.jdbc.Driver,其他都在sql包下),各个厂商都让自己的连接池实现了这个接口。常见的连接池:C3P0,DBCP及Tomcat自带连接池JNDI。

自定义连接池

使用上一节中的工具类
这里写图片描述

实现接口DataSource(javax.sql包下的)
MyDataSource.java

package cn.jdbc.DataSource;

import java.io.PrintWriter;
import java.net.ProtocolFamily;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;

import javax.sql.DataSource;
import javax.sql.PooledConnection;

import cn.jdbc.utils.JDBCUtils_V3;

//实现接口DataSource(javax.sql包下的)
public class MyDataSource implements DataSource {
    //1.创建一个容器,由于经常添加删除,选用linkedlist
    private static LinkedList<Connection> pool = new LinkedList<Connection>();

    //2.创建5个连接放到容器里
    static {
        for(int i=0;i<5;i++) {
            Connection connection = JDBCUtils_V3.getConnection();
            pool.add(connection);
        }
    }

    /*
     * 重写获取连接的方法(non-Javadoc)
     * @see javax.sql.DataSource#getConnection()
     */
    @Override
    public Connection getConnection() throws SQLException {
        Connection connection = null;
        //3.如果池子里没有,再创建些
        if(pool.size()==0) {
            for(int i=0;i<5;i++) {
                connection = JDBCUtils_V3.getConnection();
                pool.add(connection);
            }
        }
        //4.从池子里拿一个连接对象,这里拿的是第一个
        connection = pool.remove(0);
        return connection;
    }

    /*
     * 归还连接对象到连接池(non-Javadoc)
     * @see javax.sql.CommonDataSource#getLogWriter()
     */

    public void backConnection(Connection connection) {
        pool.add(connection);
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }



    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

}

TestMyDataSource.java

package cn.jdbc.test;

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import cn.jdbc.DataSource.MyDataSource;

public class TestMyDataSource {

    @Test
    public void testAdd(){
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        MyDataSource dataSource = new MyDataSource();
        try {
            connection = dataSource.getConnection();
            String sql="insert into table_user values(null,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, "Tom");
            preparedStatement.setString(2, "password");
            int rows=preparedStatement.executeUpdate();
            if(rows>0) {
                System.out.println("插入成功!");
            }else {
                System.out.println("插入失败!");
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            dataSource.backConnection(connection);
        }
    }
}

上述实现连接池,释放资源(归还连接)的时候是自己写的backConnection方法,如果想通过依旧调用close方法实现归还连接,要对close方法进行改进(增强),方法增强的办法有:继承、装饰者模式、动态代理、字节码增强。下面使用装饰者模式进行方法增强。

以本例子说明,装饰者模式,就是写一个Connection接口的实现类MyConnection,在MyConnection中写一个构造方法,用到Connection的地方,就把Connection当成参数传参,从而使用MyConnection(其实是多态的思想,Connection接口的实现类MyConnection赋给了Connection),实现移花接木。注意在MyConnection中重新书写需要增强的方法close,从而调用MyConnection的close方法来实现归还连接到连接池。

注意修改的地方:

  • 书写MyConnection类,注意那个prepareStatement方法一定要覆盖重写
  • 改MyDataSource中的更改
  • TestMyDataSource类中可以直接用close来释放资源了

这里写图片描述

MyConnection.java

package cn.jdbc.DataSource;

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.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

/*
 * 装饰者模式进行方法增强
 */

//1.实现一个接口
public class MyConnection implements Connection {

    private Connection connection;
    private LinkedList<Connection> pool;

    //2.写一个构造方法
    public MyConnection(Connection connection,LinkedList<Connection>pool) {
        this.connection=connection;
        this.pool=pool;
    }

    //3.书写要增强的方法
    @Override
    public void close() throws SQLException {
        pool.add(connection);

    }

    /*
     *此方法必须覆盖掉。否则,由于本方法原始一直返回null,会造成空指针异常(non-Javadoc)
     * @see java.sql.Connection#prepareStatement(java.lang.String)
     */
    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Statement createStatement() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }



    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void commit() throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void rollback() throws SQLException {
        // TODO Auto-generated method stub

    }



    @Override
    public boolean isClosed() throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean isReadOnly() throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public String getCatalog() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void clearWarnings() throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
            throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setHoldability(int holdability) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getHoldability() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
            throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Clob createClob() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Blob createBlob() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public NClob createNClob() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        // TODO Auto-generated method stub

    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setSchema(String schema) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public String getSchema() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void abort(Executor executor) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

}

MyDataSource.java

package cn.jdbc.DataSource;

import java.io.PrintWriter;
import java.net.ProtocolFamily;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;

import javax.sql.DataSource;
import javax.sql.PooledConnection;

import cn.jdbc.utils.JDBCUtils_V3;

//实现接口DataSource(javax.sql包下的)
public class MyDataSource implements DataSource {
    // 1.创建一个容器,由于经常添加删除,选用linkedlist
    private static LinkedList<Connection> pool = new LinkedList<Connection>();

    // 2.创建5个连接放到容器里
    static {
        for (int i = 0; i < 5; i++) {
            Connection connection = JDBCUtils_V3.getConnection();
            // 这里就是装饰模式要搞的了,这里就是搞成MyConnection的东西了,就是在种类进行移花接木了
            MyConnection myConnection = new MyConnection(connection, pool);
            pool.add(myConnection);// 其实这里是多态的用法了,Connection的实现类MyConnection赋给了Connection
        }
    }

    /*
     * 重写获取连接的方法(non-Javadoc)
     * 
     * @see javax.sql.DataSource#getConnection()
     */
    @Override
    public Connection getConnection() throws SQLException {
        Connection connection = null;
        // 3.如果池子里没有,再创建些
        if (pool.size() == 0) {
            for (int i = 0; i < 5; i++) {
                connection = JDBCUtils_V3.getConnection();
                // 这里就是装饰模式要搞的了,这里就是搞成MyConnection的东西了,就是在种类进行移花接木了
                MyConnection myConnection = new MyConnection(connection, pool);
                pool.add(myConnection);// 其实这里是多态的用法了,Connection的实现类MyConnection赋给了Connection

            }
        }
        // 4.从池子里拿一个连接对象,这里拿的是第一个
        connection = pool.remove(0);
        return connection;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

}

TestMyDataSource.java

package cn.jdbc.test;

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import cn.jdbc.DataSource.MyDataSource;
import cn.jdbc.utils.JDBCUtils_V3;

public class TestMyDataSource {

    @Test
    public void testAdd(){
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        MyDataSource dataSource = new MyDataSource();
        try {
            connection = dataSource.getConnection();
            String sql="insert into table_user values(null,?,?)";
            //因为现在的connection已经是MyConnection,所以要重新写一下MyConnection的prepareStatement方法
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, "Jack");
            preparedStatement.setString(2, "Jack__");
            int rows=preparedStatement.executeUpdate();
            if(rows>0) {
                System.out.println("插入成功!");
            }else {
                System.out.println("插入失败!");
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            JDBCUtils_V3.release(connection, preparedStatement, null);
        }
    }
}

C3P0连接池

C3P0是开源免费的连接池!使用第三方工具需要导入jar包,并添加配置文件c3p0-config.xml(名字必须是这个)。c3p0配置文件常用配置信息可以在官方文档中复制过来(为方便,可以收藏直接使用)。下面是一个包含常用信息的配置文件。

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

<!-- 这里使用默认配置,没有指定配置名字 -->
  <default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql:///web_08</property>
    <property name="user">root</property>
    <property name="password">123</property>
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">20</property>
  </default-config>

  <!--这里是指定名字为"oracle"的配置  -->
  <named-config name="oracle"> 
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql:///web_07</property>
    <property name="user">root</property>
    <property name="password">123</property>
  </named-config>


</c3p0-config>

注意上面有两个配置,一个是有名字为”oracle”,另一个是默认不带名字的。区别在于,用下面代码调用c3p0连接池,如果没有参数,默认调用默认配置,如有参数,则去找对应的名字的配置,笔者发现如果找不到对应名字的配置还是使用默认配置。

ComboPooledDataSource dataSource = new ComboPooledDataSource();

这里写图片描述
c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

<!-- 这里使用默认配置,没有指定配置名字 -->
  <default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql:///web_08</property>
    <property name="user">root</property>
    <property name="password">123</property>
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">20</property>
  </default-config>

  <!--这里是指定名字为"oracle"的配置  -->
  <named-config name="oracle"> 
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql:///web_08</property>
    <property name="user">root</property>
    <property name="password">123</property>
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">20</property>
  </named-config>


</c3p0-config>

C3P0Utils.java

package cn.jdbc.utils;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Utils {
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("oracle");
    private static DataSource getDataSource() {
        return dataSource;
    }

    public static Connection getConnection() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

TestC3P0.java

package cn.jdbc.test;

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import cn.jdbc.utils.C3P0Utils;
import cn.jdbc.utils.JDBCUtils_V3;

public class TestC3P0 {
    @Test
    public void testAdd() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = C3P0Utils.getConnection();//直接调用工具类的静态方法
            String sql = "insert into table_user values(null,?,?)";
            // 因为现在的connection已经是MyConnection,所以要重新写一下MyConnection的prepareStatement方法
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, "hello");
            preparedStatement.setString(2, "nihao");
            int rows = preparedStatement.executeUpdate();
            if (rows > 0) {

                System.out.println("插入成功!");
            } else {
                System.out.println("插入失败!");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils_V3.release(connection, preparedStatement, null);
        }
    }
}

笔者注意到上面释放连接的时候用的还是之前的工具类里面的。不知道能不能用c3p0自带的释放连接???

JDBCUtils_V3.release(connection, preparedStatement, null);

DBCP连接池

导入**两个**jar包,用properties文件配置,建议放在src下,不支持用STS修改,只能用记事本修改,否则可能出现中文乱码。
这里写图片描述

DBCPUtils.java

package cn.jdbc.utils;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtils {
    private static DataSource dataSource;
    static {
        try {
            // 1.找到properties文件
            InputStream iStream = DBCPUtils.class.getClassLoader().getResourceAsStream("db.properties");
            // 2.加载输入流
            Properties props = new Properties();
            props.load(iStream);
            // 3.创建数据项
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static DataSource getDataSource() {
        return dataSource;
    }

    public static Connection getConnection() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

TestDBCP.java

package cn.jdbc.test;

import java.sql.Connection;
import java.sql.PreparedStatement;

import org.junit.Test;

import cn.jdbc.utils.DBCPUtils;

public class TestDBCP {

    @Test
    public void testUpdateUserById(){
        Connection connection=null;
        PreparedStatement preparedStatement=null;
        try {
            connection=DBCPUtils.getConnection();
            String sql="update table_user set user_password=? where user_id=?";
            preparedStatement=connection.prepareStatement(sql);
            preparedStatement.setString(1, "haha");
            preparedStatement.setString(2, "2");
            int rows=preparedStatement.executeUpdate();
            if(rows>0) {
                System.out.println("更新成功!");
            }else {
                System.out.println("更新失败!");
            }
        }catch (Exception e) {
            throw new RuntimeException(e);      }
    }
}

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/web_08?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123

猜你喜欢

转载自blog.csdn.net/ccnuacmhdu/article/details/80965021