您的位置:首页 > 运维架构 > Tomcat

JDBC以及Tomcat连接池

2009-08-06 14:32 393 查看

JDBC连接数据库

JDBC基础

JDBC是用来与数据库进行交互的一个通用接口,它是通过数据库端的CLI(Call Level Interface)连接到处理SQL语句的引擎来与数据库进行交互的,其运行时的流程可以用下图来表示:



图中各组成部分的说明:
JDBC library
我认为这是用来管理JDBC driver的,因为JDBC要屏蔽数据源的多样性,因此 它需要通过JDBC library来管理系统中注册的JDBC driver,并且根据配置的driver来 连接数据库。
JDBC driver
数据库驱动的主要作用是将程序语言(如Java)映射为相应数据库的SQL语言。
SQL CLI
用来将SQL语句转为对数据库操作的指令,是数据库内部的东西,我猜的。

JDBC的设计目标是用来完成以下操作:
² 接收JDBC API的请求并将他们转为SQL请求
² 将这个请求提交给RDBMS上的SQL处理引擎
² 将返回的结果转为Java数据结构

建立与终止到RDBMS的连接

JDBC版本的演进

JDBC1
在这个版本的JDBC中,连接(和断开连接)数据库所需的全部代码都需要程序员去编写,甚至选择和激活JDBC驱动程序也需要程序员来手动控制。
这种方式是“硬编码”的方式,它往往只针对某一种数据库产品进行编程,不是很灵活,不能屏蔽数据库系统的差异。
JDBC2
JDBC2的一个重大改进是data source概念的引入。程序员可以从data source中获得数据库的连接,从而屏蔽了数据库系统(驱动程序)的差异。这中透明是通过对不同的data source配置来实现,而不必修改任何代码。
另一个改进是数据库连接池(connection pool)。数据库连接池的出现与网络的发展密不可分,随网络的普及,人们需要更好地处理并发操作,这样,一种可靠地,能更好地利用资源的数据库连接方式成为很显著的一种需求。于是,人们开发了很多类似数据库连接池的工具,可由于缺乏统一的规范,这些工具
JDBC3
显著地改进在于提出了业务逻辑与具体的资源相分离的结构。在JDBC3中,正式提出了他们倾向于使用JNDI来获得data source ,使用JNDI就可以做到将业务逻辑与数据库层相分离。并且在JDBC3中,它还正式确立了数据库连接池是应用服务器或Servlet容器应该提供的功能。
JDBC4
JDBC4强调降低开发者的难度,增强数据库连接池,使程序员可以获得数据表的行ID(row ID),并为JDBC提供了一种新的XML数据类型。


JDBC驱动的类型

类型1
这种是最原始的JDBC。通过适配(adapt)其它的数据获取机制(如ODBC)实现。
类型2
这种驱动部分是用Java写的,部分是用C/C++等本地数据获取语言写的。
类型3
这种类型的驱动在客户端是纯Java的。但这种方式需要中间件服务器的支持。客户端代码与在外部运行的中间件引擎进行通信,通过引擎来处理对不同数据库的连接。
类型4
这种驱动是纯Java的,并且不需要经过中间件服务器,而是通过RDBMS支持的网络协议直接与数据库进行交互。










数据库连接池

Web应用程序可以直接连接到数据库,也可以通过JDBC的方式连接到数据库。一般地,JDBC连接是将Web应用程序时通过TCP/IP协议通过网络与数据库进行连接的。这种类型连接的建立是要消耗很多资源的,一个典型的物理数据库连接需要几秒钟的时间。因此,减少连接和断开连接的需求就产生了。
现代的Web应用程序几乎在每次http请求中都会有对数据库中数据的请求。在一个负荷比较重的服务器上,连接,断开连接,重新连接...这些操作将耗费大量的资源,并显著降低系统的性能。
数据库连接池通过在服务器启动时就创建一个拥有很多物理连接的“池”来减少连接,断开连接与重新连接数据库的开销。当一个应用程序请求数据库连接时,就从连接池中取出一个“池”中的连接给它,当一个应用程序使用完一个数据库连接时,就将它断开。这里的“断开”是在逻辑上的,物理连接并没有断开,只是又回收到了“池”中,重复利用。下图为数据库连接池的示意图:



Pool Manager的作用是创建数据库连接池,管理Web应用程序在逻辑上跟数据库的物理连接的配置,并处理异常,如超时等。Pool Manager一般可以从这些地方获得:
² 应用服务器,如Tomcat6
² 第三方软件
² JDBC驱动提供商

Tomcat与JDBC的演变

Tomcat6提供对JDBC3的全面支持以前对JDBC2与JDBC1的兼容。Tomcat6中新的JDBC特性主要有以下这几个方面:
² 应用服务器管理的数据库连接池。Tomcat6使用Jakarta Commons Database Connection Pooling(DBCP)来实现应用服务器管理的数据库连接池。
² 使用JNDI-API来查找在一个应用服务器中的数据源。Tomcat6 emulate JNDI for Web applications running under it.这种获得data source的方式是可移植的,并且是易于配置的。它使得对数据库驱动的选择和数据库系统的选择可以推迟到部署时才做(因为不同的数据源仅是配置文件有些区别而已)。
² 消除不同的数据库连接器之间的差异。通过JDBC的lookup功能,Tomcat6解耦了对数据源的连接,这也是向JCA Connector-based结构迁移的第一步。

Tomcat6中的JNDI Emulation和数据库连接池

Tomcat6使得在其中运行的Web应用程序具有以下这些功能:
² 使用标准的JNDI lookup获取数据库连接
² 提供了数据库连接池功能



图中这两步分别为:
1. Web应用程序获得一个JNDI的InitialContext,然后通过name来对这个data source(JNDI data source)进行lookup操作。
2. Tomcat根据配置文件(context.xml, server.xml,和web.xml)来决定使用什么样的JDBC driver来连接data source,从而应答JNDI lookup请求。Tomcat也可以使用使用数据库连接池来pool the connections made;从Tomcat获得的数据库连接为逻辑上的数据库连接。

尽管没有使用真正的JNDI兼容的目录服务,但Tomcat模仿了JNDI的功能。这样那些使用JNDI的应用程序还能照常在Tomcat中运行。

推荐配置:JNDI资源

在很长的时期内,使用这种方式的Web应用程序都能保证与其他程序的兼容性。
下面是使用JNDI连接JDBC data source所必须做的配置工作:

找到context.xml,在相应Web程序<Context>标签内嵌入一个<Resource>标签;或者在server.xml文件中的<DefaultContext>中的<Host>标签下添加这个<Resource>标签。

在web.xml中定义与第一步中的<Resource>标签对应的的<resource-ref>标签。

在程序代码中使用lookup获得JDBC data source。

Resource标签

一个resource标签是用来表示一个JNDI data source, 这个JNDI data source表示一个JDBC data source,下面是在server.xml中的一段示例代码:
<Resource name=”jdbc/WroxTC6” auth=”Container”
type=”javax.sql.DataSource”
maxActive=”20”
maxIdle=”30”
maxWait=”10000”
username=”empro”
password=”empass”
driverClassName=”com.mysql.jdbc.Driver”
url=”jdbc:mysql://localhost:3306/wroxtomcat?autoReconnect=true”
/>

Resource标签中的每个属性的含义在下表中表示:



name="jdbc/WroxTC6" 创建一个可以从Context(程序的逻辑名)访问的JNDI data source。 java:comp/env是所有的Tomcat下的Context的前缀(完整路径 java:comp/env/jdbc/WroxTC6)。Web应用程序可以使用这个Context来lookup the data source。

auth="Container" auth属性是用来指定是由Tomcat代替应用程序执行验证任务(auth="Container") 或者由应用程序自身进行验证(auth="Application"

type="javax.sql.DataSource" type属性用来指定lookup返回的数据类型为"javax.sql.DataSource",它同 时也表明Tomcat容器将代替应用程序执行登录数据库的身份验证任务。

maxActive 在连接池中active connection的最大数目,0代表无数。

maxIdle 在连接池中空闲连接数目的上限,一旦达到这个限制,Tomcat将开始清理这些 连接。-1代表无限。

maxWait Tomcat Manager等待数据库响应的最长时间限制,一旦超过了这个限制, Manager将返回一个异常。-1表示可以永远等待。

username=“Test” 数据库账户用户名

password=“Test” 数据库账户密码

driverClassName="" 数据库驱动,Tomcat/DBCP根据这个属性来加载相应的驱动

url="" JDBC url

DBCP--Jakarta Commons Pooling Support

Tomcat6使用data source工厂来创建一个JDBC data source并将其返回给Web应用程序。Tomcat6使用DBCP来实现data source工厂以及数据库连接池。



JNDI资源配置实战

这次操作的目的是依靠Tomcat6对JNDI资源的支持,使用JDBC4驱动,来配置一个DBCP data source。

创建一个数据库,并创建一个表格。

创建一个只读用户(只对某张表有select权限)。

向默认Context中加入JDBC JNDI Resource

在context.xml中加入<Resource>标签。这一步的目的是使应用程序可以访问JNDI data source。

<Context>
<Resource name=”jdbc/WroxTC6”
auth=”Container”
type=”javax.sql.DataSource”
maxActive=”20”
maxIdle=”30”
maxWait=”10000”
username=”empro”
password=”empass”
driverClassName=”com.mysql.jdbc.Driver”
url=”jdbc:mysql://localhost:3306/wroxtomcat?autoReconnect=true”
/>
</Context>


2. 向web.xml中加入<resource-ref>标签。<resource-ref>标签使得Web应用程序可以通过JNDI API获得jdbc/wroxTC6 context。

<env-entry>
<env-entry-name>foo/name4</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>10</env-entry-value>
</env-entry>
<resource-ref>
<res-ref-name>jdbc/WroxTC6</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>


3. 使用JNDI来lookup一个data source,并增加一个出错时显示的页面。

<%@ taglib uri=”http://java.sun.com/jsp/jstl/sql” prefix=”sql” %>
<%@ taglib uri=”http://java.sun.com/jsp/jstl/core” prefix=”c” %>
<%@ page errorPage=”errorpg.jsp” %>
<html>
<head>
<sql:query var=”employees” dataSource=”jdbc/WroxTC6”>
select * from employee;
</sql:query>
</head>
<body>
<h1>JDBC JNDI Resource Test</h1>
<table width=’600Ð border=’1Ð>
<tr>
<th align=’left’>Employee ID</th>
<th align=’left’>Name</th>
<th align=’left’>Department</th>
</tr>
<c:forEach var=”employee” items=”${employees.rows}”>
<tr>
<td> ${employee.employeeid}</td>
<td> ${employee.name} </td>
<td> ${employee.department} </td>
</tr>
</c:forEach>
</table>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*"%>
<%@ page import="javax.naming.*"%>
<%
   try {
      Context initCtx = new InitialContext();
      Context envCtx = (Context) initCtx.lookup("java:comp/env");
      DataSource ds = (DataSource) envCtx.lookup("jdbc/test");
      Connection conn = ds.getConnection();

      Statement stmt = conn.createStatement();
      ResultSet rst = stmt.executeQuery("describe host");
      while (rst.next()) {
         out.println(rst.getString(1));
      }
      conn.close();
   } catch (Exception e) {
      e.printStackTrace();
   }
%>


错误页面:

<html>
<body>
<%@ page isErrorPage=”true” %>
<h1> An error has occurred </h1>
<%= exception.getMessage() %>
</body>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: