数据库连接池

      JDBC的弊端

      用户每次请求都需要向数据库获得连接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设一个网站每天有10万次访问量,那么数据库服务器就需要创建10万次连接,这极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、脱机

      为了优化性能,应用程序一启动,就向数据库要一批连接放到池(也就是一个集合而已)中,当用户箱操作数据库的时候,直接找连接池要,当完成对数据库的操作后,再把连接还给连接池供其他用户使用。 

 public class JdbcPool implements DataSource {

        private static LinkedList<Connection> list = new LinkedList<Connection>();
        static{
            try{
                InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
                Properties prop = new Properties();
                prop.load(in);//加载配置文件

                String driver = prop.getProperty("driver");
                String url = prop.getProperty("url");
                String username = prop.getProperty("username");
                String password = prop.getProperty("password");

                Class.forName(driver); //加载驱动
                //连接池中创建10个Connection
                for(int i = 0; i < 10; i++){ 
                    Connection conn = DriverManager.getConnection(url, username, password);
                    System.out.println("获取到了连接" + conn);
                    list.add(conn);
                }
            } catch(Exception e){
                e.printStackTrace();
                throw new ExceptionInInitializerError(e);
            }
        }

        /*
         * 用动态代理,返回一个代理对象出去,拦截close方法的调用,对close进行增强
         */ 
        @Override
        public synchronized Connection getConnection() throws SQLException {
            if(list.size() > 0){
                final Connection conn =  list.removeFirst();//删掉并返回给conn
    //          return conn;//这里不能直接return,因为用户使用完了后,调用conn.close()会操作数据库,并没有把这个conn返回给连接池中
                System.out.println("池大小是" + list.size());

                //下面用动态代理技术来写:
                //动态代理技术:使用的是拦截技术
                return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
                //这里的第二个参数原来为conn.getClass.getInterface(),不过会出错,最终改成了new Class[]{Connection.class}
                //原因见帖子:http://blog.csdn.net/njchenyi/article/details/3091092
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {  

                        if(!method.getName().equals("close")){//如果判断不是调用close方法,不管就是了
                            return method.invoke(conn, args);
                        }
                        else{//如果是调用close方法,将conn放到连接池里
                            list.add(conn);
                            System.out.println(conn + "被还到池中");
                            System.out.println("池大小为" + list.size());
                            return null;
                        }
                    }
                });

            }
            else{
                throw new RuntimeException("对不起,数据库忙");
            }       
        }
        //下面是其他需要实现的方法,默认生成即可,不用写代码
        @Override
        ......
    }

       数据库连接池

      很多web服务器(Weblogic,WebSphere, Tomcat)都提供DataSource的实现,即连接池的实现。通常我们把DataSource的实现,称之为数据源,数据源中都包含了数据库连接池的实现。也有一些开源组织提供了数据源的独立实现:如DBCP数据库连接池和C3P0数据库连接池(Spring中用的就是这个)。使用这些连接池时不需要编写连接数据库的代码,直接从数据源获得数据库的连接,程序员编程时也尽量使用这些数据源的实现,以提升程序的数据库访问性能。
      DBCP是Apache软件基金组织下的开源连接池实现,使用DBCP数据源需要在程序中加入下面两个jar包:

  • commons-dbcp.jar:连接池的实现;
  • commons-pool.jar:连接池实现的依赖库

     Tomcat的连接池正是采用DBCP连接池来实现的,该数据库连接池既可以与应用服务器整合使用,也可以独立使用,下面我们使用DBCP来改写上面的JdbcUtils工具类:

public class JdbcUtils_DBCP {

    private static DataSource ds = null;//定义数据源 
    static{     
        try{
            InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp.properties");
            Properties prop = new Properties();
            prop.load(in);

            BasicDataSourceFactory factory = new BasicDataSourceFactory();//数据源工厂
            ds = factory.createDataSource(prop);//工厂产生数据源
            System.out.println(ds);

        }catch(Exception e){
            throw new ExceptionInInitializerError(e);
        }       

    }

    public static synchronized Connection getConnection() throws SQLException {     
        Connection conn = ds.getConnection();//从数据源中拿一个Connection来用~
        return conn;
    }

    public static void release(Connection conn, Statement st, ResultSet rs) {       
        if(rs != null){
            try{
                rs.close();
            }catch(Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st != null){
            try{
                st.close();
            }catch(Exception e) {
                e.printStackTrace();
            }
            st = null;
        }

        if(conn != null){
            try{
                conn.close();
            }catch(Exception e) {
                e.printStackTrace();
            }
            conn = null;
        }   
    }
}

       从上面代码中可以看出,获得数据源dataSource后,就可以直接通过数据源拿到Connection,说明拿之前连接池中已经放好了一些Connection了,这些都已经被DBCP封装好,我们不用去管,我们需要做的就是在配置文件中做一些配置,DBCP会根据配置文件中的配置去初始化数据库连接池的。我们看一下都需要配置啥:

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/databasename
username=root
password=root

#初始化连接:10个
initialSize=10

#最大连接数量
maxActive=50

#最大空闲连接
maxIdle=20

#最小空闲连接
minIdle=5

#超时等待时间以毫秒为单位 6000毫秒/1000等于60秒
maxWait=60000 

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 
#注意:"user""password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

     

C3P0连接池

  用C3P0数据库连接池,需要导入下面两个jar包

 

c3p0-0.9.5.1.jar
mchange-commons-java-0.2.10.jar

 

     使用C3P0来改写JdbcUtils工具类了:

public class JdbcUtils_C3P0 {

    private static ComboPooledDataSource ds = null;

    static {

        try {
            //配置文件可以用properties文件,也可以用xml,这里使用xml配置,下面给出配置好的xml(要放在类路径下)
            ds = new ComboPooledDataSource(); //使用默认配置
            //ds = new ComboPooledDataSource("mysql"); //指定配置

        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }

    }

    public static synchronized Connection getConnection() throws SQLException {
        Connection conn = ds.getConnection();
        return conn;
    }

    public static void release(Connection conn, Statement st, ResultSet rs) {....}
}

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://localhost:3306/databasename</property>
                <property name="user">root</property>
                <property name="password">root</property>

                <property name="acquireIncrement">5</property>
                <property name="initialPoolSize">10</property>
                <property name="minPoolSize">5</property>
                <property name="maxPoolSize">20</property> <!-- intergalactoApp adopts a different approach to configuring statement 
                    caching -->
                <property name="maxStatements">0</property>
                <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him -->
            </default-config>

            <named-config name="mysql"> <!--mysql的配置,在new ComboPooledDataSource()时候括号中指定,所以还可以再对oracle进行配置-->
                <property name="driverClass">com.mysql.jdbc.Driver</property>
                <property name="jdbcUrl">jdbc:mysql://localhost:3306/databasename</property>
                <property name="user">root</property>
                <property name="password">root</property>

                <property name="acquireIncrement">5</property>
                <property name="initialPoolSize">10</property>
                <property name="minPoolSize">5</property>
                <property name="maxPoolSize">20</property> <!-- intergalactoApp adopts a different approach to configuring statement 
                    caching -->
                <property name="maxStatements">0</property>
                <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him -->
            </named-config>
        </c3p0-config>

配置Tomcat数据源

      Tomcat服务器在启动时可以帮我们创建一个池,这样我们可以直接利用这个连接池,但是需要进行配置。在META-INF目录下新建一个context.xml文档(也可以在WEB-INF目录下的web.xml中进行配置),然后在里面进行如下配置:

<Context>
    <Resource name="jdbc/EmployeeDB"
        auth="Container"
        type="javax.sql.DataSource"

        username="root"
        password="root"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/day16"
        maxTotal="8"
        maxIdle="4"/>
</Context>

其他参数可以参照dbcp的配置文件进行设置,因为tomcat连接池内部就是dbcp。然后新建一个servlet,在servlet中编写如下代码(Tomcat连接池的模板代码):

try {
    Context initCtx = new InitialContext();// 初始化jndi
    Context envCtx = (Context) initCtx.lookup("java:comp/env");// 得到jndi容器
    DataSource ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");// 从容器中检索连接池
    Connection conn = ds.getConnection();
    System.out.println(conn);
} catch (Exception e) {
    e.printStackTrace();
}

 

 

参考:

https://blog.csdn.net/eson_15/article/details/51454428

posted on 2018-10-12 18:30  溪水静幽  阅读(155)  评论(0)    收藏  举报