关闭

开源数据库连接池

标签: Java-Web基础
1462人阅读 评论(1) 收藏 举报
分类:

现在很多WEB服务器(Weblogic、WebSphere、Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:

  • DBCP数据库连接池。
  • C3P0数据库连接池。
  • Tomcat内置的连接池(其实使用的是Apache DBCP)

实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。

DBCP数据源

DBCP是Apache软件基金组织下的开源连接池实现,要使用DBCP数据源,需要应用程序在系统中增加如下两个jar文件:

  • commons-dbcp-1.4.jar:连接池的实现。
  • commons-pool-1.6.jar:连接池实现的依赖库。
    注意:commons-pool已经到2.0时代了。但是如果使用pool2的包的话,会说找不到类,因为包结构已经变了,不知道怎么配过去,还望赐教啊!!!

Tomcat的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。

在应用程序中加入dbcp连接池

导入相关jar包:

  • commons-dbcp-1.4.jar
  • commons-pool-1.6.jar

在类目录下加入dbcp的配置文件:dbcpconfig.properties。dbcpconfig.properties的配置信息如下:

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

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

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


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

#指定由连接池所创建的连接的自动提交(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_COMMITTED

如下图所示:
这里写图片描述
在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池。

public class JdbcUtils_DBCP {

    private static DataSource ds = null;

    // 静态代码块只执行一次,因为静态代码块在类加载时执行,类永远只加载一次
    static {
        // 初始化连接池

        try {
            InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties prop = new Properties();
            prop.load(in);

            BasicDataSourceFactory factory = new BasicDataSourceFactory();
            ds = factory.createDataSource(prop);
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        return ds.getConnection(); // 不会将真正的MySQL驱动返回的Connection返回给你
    }

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

        if (rs!=null) {
            try {
                rs.close(); // 假设throw异常
            } catch (Exception e) {
                e.printStackTrace(); // 只需在后台记录异常
            }
            rs = null; // 假设rs对象没有释放,将其置为null,该对象就变成垃圾,由Java垃圾回收器回收
        }
        if (st!=null) {
            try {
                st.close(); // 假设throw异常
            } catch (Exception e) {
                e.printStackTrace(); // 只需在后台记录异常
            }
            st = null;
        }
        if (conn!=null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace(); // 只需在后台记录异常
            }
        }

    }
}

测试DBCP数据源。

public class Demo {

    public static void main(String[] args) throws SQLException, InterruptedException {

        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils_DBCP.getConnection();
            System.out.println(conn.getClass().getName());
        } finally {
            JdbcUtils_DBCP.release(conn, st, rs);
        }
    }

}

运行以上程序,在Eclipse的控制台打印:

org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper

C3P0数据源(Spring内置)

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。C3P0数据源在项目开发中使用得比较多。
c3p0与dbcp的区别:

  • dbcp没有自动回收空闲连接的功能。
  • c3p0有自动回收空闲连接的功能。

在应用程序中加入C3P0连接池

导入相关jar包。

  • c3p0-0.9.5.2.jar。
  • mysql-connector-java-5.1.38-bin.jar
    注意:如果操作的是Oracle数据库,那么还需要导入c3p0-oracle-thin-extras-0.9.5.2.jar。

在类目录下加入C3P0的配置文件:c3p0-config.xml。关于该配置文件怎么写,可以参考C3P0数据源的文档。在下载并解压的c3p0-0.9.5.2文件夹中打开c3p0-0.9.5.2\doc下的index.html页面,找到如下的位置:
这里写图片描述
就能知道c3p0-config.xml文件怎么编写了,并且该配置文件的名称一定得是c3p0-config
我的c3p0-config.xml文件中的配置信息是这样写的:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 
        C3P0的缺省(默认)配置, 
        如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源
    -->
    <default-config>
           <!-- C3P0的属性:driverClass -->     
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">yezi</property>

        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property> <!-- 最大空闲时间 -->
        <property name="maxPoolSize">20</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </default-config>
    <!-- 
        C3P0的命名配置,
        如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource("mysql");”这样写就表示使用的是name是mysql的配置信息来创建数据源
    -->
    <named-config name="mysql">
        <property name="acquireIncrement">50</property>
        <property name="initialPoolSize">100</property>
        <property name="minPoolSize">50</property>
        <property name="maxPoolSize">1000</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>

    <named-config name="oracle">
        <property name="acquireIncrement">50</property>
        <property name="initialPoolSize">100</property>
        <property name="minPoolSize">50</property>
        <property name="maxPoolSize">1000</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>

如下图所示:
这里写图片描述
在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池。

public class JdbcUtils_C3P0 {

    private static ComboPooledDataSource ds = null;

    // 静态代码块只执行一次,因为静态代码块在类加载时执行,类永远只加载一次
    static {
        // 初始化连接池
        try {
            // 通过读取C3P0的xml配置文件创建数据源,C3P0的xml配置文件c3p0-config.xml必须放在src目录下
            ds = new ComboPooledDataSource(); // 使用配置文件的缺省配置,配置文件名称必须是c3p0-config.xml

            // 通过代码创建C3P0数据库连接池
            /*
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/day16");
            ds.setUser("root");
            ds.setPassword("yezi");
            ds.setMaxPoolSize(30);
            ds.setMinPoolSize(5);
            ds.setInitialPoolSize(10);
            */
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        return ds.getConnection(); // 不会将真正的MySQL驱动返回的Connection返回给你
    }

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

        if (rs!=null) {
            try {
                rs.close(); // 假设throw异常
            } catch (Exception e) {
                e.printStackTrace(); // 只需在后台记录异常
            }
            rs = null; // 假设rs对象没有释放,将其置为null,该对象就变成垃圾,由Java垃圾回收器回收
        }
        if (st!=null) {
            try {
                st.close(); // 假设throw异常
            } catch (Exception e) {
                e.printStackTrace(); // 只需在后台记录异常
            }
            st = null;
        }
        if (conn!=null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace(); // 只需在后台记录异常
            }
        }

    }
}

测试C3P0数据源。

public class Demo {

    public static void main(String[] args) throws SQLException, InterruptedException {

        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils_C3P0.getConnection();
            System.out.println(conn.getClass().getName());
        } finally {
            JdbcUtils_DBCP.release(conn, st, rs);
        }
    }

}

运行以上程序,在Eclipse的控制台打印:
这里写图片描述

配置Tomcat数据源

在实际开发中,我们有时候还会使用服务器提供给我们的数据库连接池,比如我们希望Tomcat服务器在启动的时候可以帮我们创建一个数据库连接池,那么我们在应用程序中就不需要手动去创建数据库连接池,直接使用Tomcat服务器创建好的数据库连接池即可。要想让Tomcat服务器在启动的时候帮我们创建一个数据库连接池,那么需要简单配置一下Tomcat服务器。

JNDI技术简介

JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包。
这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
Tomcat服务器创建的数据源是以JNDI资源的形式发布的,所以说在Tomat服务器中配置一个数据源实际上就是在配置一个JNDI资源,通过查看Tomcat文档,我们知道使用如下的方式配置tomcat服务器的数据源:

<Context>
     <Resource name="jdbc/EmployeeDB"
            auth="Container"
            type="javax.sql.DataSource"
            username="root"
            password="yezi"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/day16"
            initialSize="10"
            maxActive="30"
            maxIdle="4"/>
</Context>

服务器创建好数据源之后,我们的应用程序又该怎么样得到这个数据源呢,Tomcat服务器创建好数据源之后是以JNDI的形式绑定到一个JNDI容器中的,我们可以把JNDI想象成一个大大的容器,我们可以往这个容器中存放一些对象,一些资源,JNDI容器中存放的对象和资源都会有一个独一无二的名称,应用程序想从JNDI容器中获取资源时,只需要告诉JNDI容器要获取的资源的名称,JNDI根据名称去找到对应的资源后返回给应用程序。
总结:我们平时做javaEE开发时,服务器会为我们的应用程序创建很多资源,比如request对象,response对象,服务器创建的这些资源有两种方式提供给我们的应用程序使用

  • 第一种是通过方法参数的形式传递进来,比如我们在Servlet中写的doPost和doGet方法中使用到的request对象和response对象就是服务器以参数的形式传递给我们的。
  • 第二种就是JNDI的方式,服务器把创建好的资源绑定到JNDI容器中去,应用程序想要使用资源时,就直接从JNDI容器中获取相应的资源即可。

又可以这样总结:在服务器下做编程,我们要获取一个资源,现在多了一种新的方式。以前,一个浏览器去访问服务器时,服务器调用你的servlet,会传递一些对象给你,对象是怎么传递给你的呢?都是调用你servlet的方法时,把对象传递给你。现在我们学了这种模型之后,将来在服务器下做开发,服务器还有一种方式传对象给你,并不是在调用你servlet的方法时,把对象作为参数传递给你,而是会把对象放在一个JNDI容器里面,你需要的时候就从容器里面取
对于上面的name=”jdbc/EmployeeDB”数据源资源,在应用程序中可以用如下的代码去获取:

Context initCtx = new InitialContext(); // 初始化JNDI
Context envCtx = (Context) initCtx.lookup("java:comp/env");
dataSource = (DataSource)envCtx.lookup("jdbc/EmployeeDB");

用图来表示即为:
这里写图片描述

配置Tomcat数据源

为了配置Tomcat数据源,我们可参考Tomcat服务器文档(http://localhost:8080/docs/config/context.html),找到如下的位置:
这里写图片描述
注意上面标红的句子,我们重点关注这句话。

In an individual file at /META-INF/context.xml inside the application files.

翻译过来大致意思就是: 一个单独的文件,在应用程序文件/META-INF/context.xml内。
这句话告诉我们应该在Web项目的WebRoot目录下的META-INF目录创建一个context.xml文件
如下图所示:
这里写图片描述
更加详细的内容我们可以参考我以前的笔记JavaWeb开发入门(二)——虚拟目录的映射方式三
context.xml文件创建出来了,怎么编写里面的内容呢?所以我们又要参考Tomcat服务器的文档(http://localhost:8080/docs/jndi-resources-howto.html)了,找到如下的位置:
这里写图片描述
对应着以上代码修修改改就可以了。
现在在context.xml文件配置tomcat服务器的数据源

<?xml version="1.0" encoding="UTF-8"?>
<Context>
     <Resource name="jdbc/EmployeeDB"
            auth="Container"
            type="javax.sql.DataSource"
            username="root"
            password="yezi"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/day16"
            initialSize="10"
            maxActive="30"
            maxIdle="4"/>
</Context>

以上为某一个web应用配置了一个资源,资源的类型是javax.sql.DataSource,即配置了一个连接池,tomcat在启动的时候就会为你的web应用创建一个连接池,并且tomcat会把这个连接池以JNDI的资源的形式绑定到jdbc/EmployeeDB这么一个名称上面去,即你的应用程序等一会要用连接池,只需根据这个名称检索就行了,就可拿到连接池。
auth="Container":该参数指定由容器来创建连接池。
接着将数据库的驱动jar文件需放置在tomcat的lib下。如下图所示:
这里写图片描述
特别提醒:此种配置下,数据库的驱动jar文件需放置在tomcat的lib下。再说一遍,千万注意,由于是服务器来创建连接池,所以说数据库驱动jar包一定要加到tomcat服务器的lib目录里面去。否则,会报异常

java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

在获取数据库连接的工具类(如jdbcUtils)的静态代码块中获取JNDI容器中的数据源
在cn.itcast.utils包下创建获取数据库连接的工具类——JdbcUtils.java,JdbcUtils类的代码如下所示:

public class JdbcUtils {

    private static DataSource ds = null;
    static {
        try {
            Context initCtx = new InitialContext();
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
            ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

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

写一个小程序来测试JNDI数据源
在cn.itcast.dao包下创建一个类——Dao.java,其代码如下所示:

public class Dao {

    public void add() {
        Connection conn = null;
        try {
            conn = JdbcUtils.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        System.out.println(conn);
    }

}

在cn.itcast.web.servlet包下创建一个Servlet——Servlet1.java,其代码如下所示:

public class Servlet1 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Dao dao = new Dao();
        dao.add();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

在浏览器中输入访问服务器的地址http://localhost:8080/day16_datasource/Servlet1,然后可以在Eclipse的控制台下看到如下结果:
这里写图片描述

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:549531次
    • 积分:8726
    • 等级:
    • 排名:第2444名
    • 原创:368篇
    • 转载:0篇
    • 译文:0篇
    • 评论:238条
    博客专栏