搞定J2EE核心技术与企业应用
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

7.5 JDBC高级应用——数据库连接池

当编写对于数据库的访问不是很频繁的应用程序时,可以在需要访问数据库时创建一个新连接,用完后就关闭它,这样做不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了,频繁地建立、关闭连接,会极大地降低该应用程序的性能。

7.5.1 数据库连接池简介

为了避免频繁的建立和关闭数据库,开发人员可以通过建立一个数据库连接池和一套管理策略来达到连接资源的共享,使得对于数据库的连接高效、安全。

对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。

数据库连接池的基本原理是在内部对象池中,维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如图7.27所示,当程序中需要建立数据库连接时,只需从内存中获取一个数据库连接,而不是新建一个数据库连接,在使用完毕后,只需放回内存即可。对于连接的建立和断开都由连接池自己管理。

图7.27 数据库连接池原理

综上所述,使用数据库连接池具有如下特点。

● 资源重用:为了避免频繁的创建、释放数据库连接,实现了数据库连接的重用。

● 高效的系统响应:数据库连接池在初始化过程中,一般就会创建若干个数据库连接存储在池中备用,所以具有高效的效率。

● 统一的连接管理:对于连接数目的创建、断开、管理和关闭等操作都是由数据库连接池统一管理。

7.5.2 数据库连接池原理

通过上一节可以知道数据库连接池的简单概念,这一节将通过一个简单的实例来演示如何实现数据库连接池和使用数据库连接池后的效果,在该实例中连接池是通过JDBC驱动程序来实现连接的,具体步骤如下:

01 首先创建一个名为connectionpooling的Java项目。

02 接着创建两个文件,其中ConnectionPool.java文件实现数据库连接池程序,具体内容如下:

      //******* ConnectionPool.java **************
      public class ConnectionPool {
        private Vector<Connection> pool;
        private String url;
        private String username;
        private String password;
        private String driverClassName;
        private int poolSize=1;            //连接池的大小,也就是连接池中有多少个数据库连接
        private static ConnectionPool instance = null;
        //私有的构造方法,禁止外部创建本类的对象,要想获得本类的对象,需要通过
        //<code>getIstance</code>方法
        private ConnectionPool() {
              init();
        }
        //连接池初始化方法,读取属性文件的内容建立连接池中的初始连接
        private void init() {
              pool = new Vector<Connection>(poolSize);
              readConfig();
              addConnection();
        }
        //返回连接到连接池中
        public synchronized void release(Connection conn) {
              pool.add(conn);
        }
        //关闭连接池中的所有数据库连接
        public synchronized void closePool() {
              for (int i = 0; i < pool.size(); i++) {
                try {
                    ((Connection) pool.get(i)).close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                pool.remove(i);
              }
        }
        //返回当前连接池的一个对象
        public static ConnectionPool getInstance() {
              if (instance == null) {
                instance = new ConnectionPool();
              }
              return instance;
        }
      //返回连接池中的一个数据库连接
      public synchronized Connection getConnection() {
          if (pool.size() > 0) {
              Connection conn = pool.get(0);
              pool.remove(conn);
              return conn;
          } else {
              return null;
          }
      }
      //在连接池中创建初始设置的的数据库连接
      private void addConnection() {
          Connection conn = null;
          for (int i = 0; i < poolSize; i++) {
              try {
                  Class.forName(driverClassName);
                  conn = java.sql.DriverManager.getConnection(url, username, password);
                  pool.add(conn);
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }
      //读取设置连接池的属性文件
      private void readConfig() {
          try {
              String path = System.getProperty("user.dir") + "\\dbpool.properties";
              FileInputStream is = new FileInputStream(path);
              Properties props = new Properties();
              props.load(is);
              this.driverClassName = props.getProperty("driverClassName");
              this.username = props.getProperty("username");
              this.password = props.getProperty("password");
              this.url = props.getProperty("url");
              this.poolSize = Integer.parseInt(props.getProperty("poolSize"));
          } catch (Exception e) {
              e.printStackTrace();
              System.err.println("读取属性文件出错. ");
          }
      }
      }

代码说明:

在具体开发程序时并不需要程序员自己实现数据库连接池,因为许多公司或组织已经把性能更好的数据库连接池组件集成到自己的软件中了,程序员在需要使用数据库连接池时只需要简单配置一下即可。

另一个ConnectionPoolTest.java文件用来测试连接池程序,具体内容如下:

      //******* ConnectionPoolTest.java **************
      public class ConnectionPoolTest {
        public static void main(String[] args) throws Exception {
              String sql="select*from student";                    //定义SQL语句
              long start=System.currentTimeMillis();               //定义一个时间变量
              ConnectionPool pool=null;                      //定义一个数据库连接池变量
              for (int i = 0; i < 100; i++) {
                pool=ConnectionPool.getInstance();                //初始化数据库连接池
                Connection conn=pool.getConnection();              //获取连接对象
                Statement stmt=conn.createStatement();             //创建陈述对象
                ResultSet rs=stmt.executeQuery(sql);               //执行SQL语句
                while (rs.next()) {
                }
                rs.close();                                 //关闭数据集
                stmt.close();                               //关闭陈述对象
                pool.release(conn);                               //返回连接对象
              }
              pool.closePool();                                   //关闭数据库连接池
              System.out.println("经过100次的循环调用,使用连接池花费的时间:" + (System.
              currentTimeMillis() - start) + "ms\n");
              String hostName="127.0.0.1";                        //安装数据库的计算机
              String driverClass="com.mysql.jdbc.Driver";         //加载mysql数据库驱动程序
              String url="jdbc:mysql://hostname:3306/testmysql";   //数据库URL
              String user="root";                                 //用户名
              String password="root";                              //用户密码
              start = System.currentTimeMillis();
              for (int i = 0; i < 100; i++) {
                Class.forName(driverClass);                       //加载数据库驱动
                Connection conn = DriverManager.getConnection(url, user, password);
                Statement stmt=conn.createStatement();             //创建陈述对象
                ResultSet rs=stmt.executeQuery(sql);               //执行SQL语句
                while (rs.next()) {
                }
                rs.close();
                stmt.close();
                conn.close();
              }
              System.out.println("经过100次的循环调用,不使用连接池花费的时间:" + (System.
              currentTimeMillis() - start) + "ms");
        }
      }

代码说明:

在上述代码中第一个for循环中通过数据库连接池来实现100次数据库的连接和关闭,而第二个for循环中通过直接调用JDBC驱动程序来实现100次数据库的连接和关闭。

03 在connectionpooling项目中创建一个名为dbpool.properties的属性文件,用来配置数据库连接池连接数据库的信息,具体内容如下:

      //******* dbpool.properties **************
      driverClassName=oracle.jdbc.driver.OracleDriver         //加载数据库驱动程序
      username=scott                                           //用户名
      password=root                                            //用户密码
      url=jdbc:oracle:thin:@localhost:1521:orcll               //数据库URL
      poolSize=10                                              //数据库连接数目

04 编译和运行ConnectionPoolTest程序后,其运行结果如图7.28所示。

图7.28 运行结果

注意:虽然测试程序的运行结果会根据具体的计算机性能显示的时间不一样,但是不使用数据库连接池花费的时间,基本上是使用数据库连接池的n倍。

7.5.3 配置和使用服务器Tomcat 7连接池

在开发具体项目时编写数据库连接池是没有必要,因为现在已经存在许多数据库连接池的现成组件,只需要配置一下就可以使用。而且现在许多应用服务器都已经内置了数据库连接池,如Tomcat服务器、Jboss服务器和WebLogic服务器等。

之所以能够调用已经内置好的数据库连接池,是因为JavaEE支持JNDI。JNDI的英文全称是Java Naming and Directory Interface,中文意思为“Java命名和目录服务接口”。JNDI向应用程序提供了一个查询和使用远程服务的机制。

数据源的使用方法如下:

01 在Tomcat 7中配置数据源,可以直接在server.xml文件中配置,也可以在每个项目单独的XML文件中配置,示例代码如下:

      <Resource name="JDBC/opendb" auth="Container"  type="javax.sql.DataSource" factory="org.
  apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
      driverClassName="org.postgresql.Driver" url="JDBC:postgresql://localhost/testDb" username="sa"
  password="sa" maxActive="10000"
      maxIdle="10000" maxWait="10000"  removeAbandoned="true" removeAbandonedTimeout="10"
  logAbandoned="true"
      />

02 在web.xml文件中添加如下配置:

      <resource-ref>
          <res-ref-name>JDBC/opendb</res-ref-name>
          <res-type>javax.sql.DataSource</res-type>
          <res-auth>Container</res-auth>
      </resource-ref>

03 在程序中可以使用如下代码获取数据源和数据库连接:

      Connection conn = ((DataSource) new InitialContext().lookup("java:/comp/env/jdbc/opendb")).
    getConnection();