1、DataSource接口介绍
- (1)DataSource 概述
- (2)使用DataSource的优点
关于数据源的信息和如何来定位数据源,例如数据库服务器的名字,在哪台机器上,端口号等等,都包含在DataSource对象的属性里面去了。这样,对应用程序的设计来说是更方便了,因为并不需要硬性的把驱动的名字写死到程序里面去。通常驱动名字中都包含了驱动提供商的名字,而在DriverManager类中通常是这么做的。
2,可移植性
如果数据源要移植到另一个数据库驱动中,代码也很容易做修改。所需要做的修改只是更改DataSource的相关的属性。而使用DataSource对象的代码不需要做任何改动。
- (3)配置DataSource
在下面的例子中,名字起为:WebMisDB,按照惯例,逻辑名字通常都在jdbc的子上下文中。这样,逻辑名字的全名就是:jdbc/WebMisDB。
- (4)产生一个与数据源的连接
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/WebMisDB");
Connection con = ds.getConnection("myPassword", "myUserName");
在一个基本的DataSource实现中,DataSource.getConnection方法返回的Connection对象和用DriverManager.getConnection方法返回的Connection对象是一样的。因为DataSource提供的方便性,我们推荐使用DataSource对象来得到一个Connection对象。
- (5)DataSource的应用场合
- (6)数据源(DataSource)的作用
它相当于客户端程序和连接池的中介,想要获得连接池中的连接对象,必须建立一个与该连接池相应的数据源,然后通过该数据源获得连接。
- (1)JNDI简介
- (2)获得JNDI的初始环境
Context ctx = new InitialContext ();
应用可以通过这个初始化的Context经由这个目录树来定位它所需要的资源或对象。InitialContext在网页应用程序初始化时被设置,用来支持网页应用程序组件。所有的入口和资源都放在JNDI命名空间里的java:comp/env段里。
- (3)查找已绑定的对象
import javax.naming.*; public class TestJNDI { public static void main(String[] args) { try { Context ctx=new InitialContext(); Object object=ctx.lookup(“JNDIName”); //根据JNDI名查找绑定的对象 String str=(String) object; //强制转换 } catch(NamingException e) { e.printStackTrace(); } catch(ClassCastException e) { e.printStackTrace(); } } }
3、数据库连接池技术
- (1)传统的Web数据库编程模式
进行SQL操作,取出数据。
断开数据库连接。
使用这种模式开发,存在很多问题。
首先,我们要为每一次WEB请求(例如察看某一篇文章的内容)建立一次数据库连接,对于一次或几次操作来讲,或许你觉察不到系统的开销,但是,对于WEB程序来讲,即使在某一较短的时间段内,其操作请求数也远远不是一两次,而是数十上百次(想想全世界的网友都有可能在您的网页上查找资料),在这种情况下,系统开销是相当大的。事实上,在一个基于数据库的WEB系统中,建立数据库连接的操作将是系统中代价最大的操作之一。很多时候,可能您的网站速度瓶颈就在于此。
其次,使用传统的模式,你必须去管理每一个连接,确保他们能被正确关闭,如果出现程序异常而导致某些连接未能关闭,将导致数据库系统中的内存泄露,最终我们将不得不重启数据库。
频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。
- (2)数据库连接是一种关键的有限的昂贵的资源
连接池是这么一种机制,当应用程序关闭一个Connection的时候,这个连接被回收,而不是被destroy,因为建立一个连接是一个很费资源的操作。如果能把回收的连接重新利用,会减少新创建连接的数目,显著的提高运行的性能。该策略的核心思想是:连接复用。
通过采用连接池的方法,服务器在启动时先打开一定数量的连接。 当应用需要连接时,就可以从服务器请求一个连接。当应用结束该连接时,服务器就把它释放到连接池,以备其他客户机使用。
服务器监听客户的连接请求 客户获得连接
- (3)连接池的主要作用
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;
释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
封装用户信息 使用连接池可以封装连接数据库系统所用的用户信息(帐号和密码),这样客户端程序在建立连接时不用考虑安全信息。
- (4)数据库连接池的工作原理
- (5)数据库连接池的最小连接数和最大连接数
最小连接数是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费;
最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。
如果最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。
- (6)使用连接池得到连接
Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("jdbc/EmployeeDB"); Connection con = ds.getConnection("myPassword", "myUserName"); 或者: Context ctx = new InitialContext(); ConnectionPoolDataSource ds = (ConnectionPoolDataSource)ctx.lookup("jdbc/EmployeeDB"); PooledConnection con = ds.getConnection("myPassword", "myUserName");
是否使用连接池获得一个连接,在应用程序的代码上是看不出不同的。在使用这个Connection连接上也没有什么不一样的地方,唯一的不同是在java的finally语句块中来关闭一个连接。在finally中关闭连接是一个好的编程习惯。这样,即使方法抛出异常,Connection也会被关闭并回收到连接池中去。代码应该如下所示:
try {… } catch() {… } finally { if(con!=null) con.close(); }
4、在Tomcat中配置数据库的连接池
(1)连接池配置(Database Connection Pool (DBCP) Configurations)
DBCP使用的是Jakarta-Commons Database Connection Pool 要使用连接池需要如下的组件即jar文件。
Jakarta-Commons DBCP 1.1 对应commons-dbcp-1.1.jar。
Jakarta-Commons Collections 2.0 对应commons-collections.jar。
Jakarta-Commons Pool 1.1 对应commons-pool-1.1.jar。这三个jar文件要与你的JDBC驱动程序一起放到【TOMCAT_HOME】commonlib目录下以便让tomcat和你的web应用都能够找到。
注:
这三个jar文件是默认存在与【TOMCAT_HOME】commonlib下的。
需要注意的地方:第三方的驱动程序或者其他类只能以*.jar的形式放到Tomcat的commonlib目录中,因为Tomcat只把*.jar文件加到CLASSPATH中。
不要把上诉三个文件放到WEB-INF/lib或者其他地方因为这样会引起混淆。
(2)通过配置阻止连接池漏洞
数据库连接池创建和管理连接池中建立好的数据库连接,循环使用这些连接以得到更好的效率。这样比始终为一个用户保持一个连接和为用户的请求频繁的建立和销毁数据库连接要高效的多。
这样就有一个问题出现了,一个Web应用程序必须显示的释放ResultSet,Statement和Connection。如果在关闭这些资源的过程中失败将导致这些资源永远不在可用,这就是所谓的连接池漏洞。这个漏洞最终会导致连接池中所有的连接不可用。
通过配置Jakarta Common DBCP可以跟踪和恢复那些被遗弃的数据库连接。
以下是一系列相关配置:
通过配置DBCP数据源中的参数removeAbandoned来保证删除被遗弃的连接使其可以被重新利用。
为ResourceParams(见下文的数据源配置)标签添加参数removeAbandoned
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
通过这样配置的以后当连接池中的有效连接接近用完时DBCP将试图恢复和重用被遗弃的连接。这个参数的值默认是false。
通过设置removeAbandonedTimeout来设置被遗弃的连接的超时的时间,即当一个连接连接被遗弃的时间超过设置的时间时那么它会自动转换成可利用的连接。
<parameter>
<name>removeAbandonedTimeout</name>
<value>60</value>
</parameter>
默认的超时时间是300秒。
设置logAbandoned参数,以将被遗弃的数据库连接的回收记入日志中
<parameter>
<name>logAbandoned</name>
<value>true</value>
</parameter>
这个参数默认为false。
(3)修改server.xml文件
<Context path="/WebMis" docBase="WebMis" debug="0" reloadable="true"> <Resource name="jdbc/webmis" auth="Container" type="javax.sql.DataSource"/> <ResourceParams name="jdbc/webmis"> <parameter> <name> factory </name> <value>org.apache.commons.dbcp.BasicDataSourceFactory </value> </parameter> <parameter> <name> driverClassName </name> <value>com.microsoft.jdbc.sqlserver.SQLServerDriver </value> </parameter> <parameter> <name> url </name> <value>jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=DataBase </value> </parameter> <parameter> <name> username </name> <value> sa </value> </parameter> <parameter> <name> password </name> <value> </value> </parameter> <parameter> <name> maxActive </name> <value> 20 </value> </parameter> <parameter> <name> maxIdle </name> <value>10</value> </parameter> <parameter> <name>maxWait</name> <value>-1</value> </parameter> <parameter> <name>removeAbandoned</name> <!-- Abandoned DB connections are removed and recycled --> <value>true</value> </parameter> <parameter> <name>removeAbandonedTimeout</name> <!-- Use the removeAbandonedTimeout parameter to set the number of seconds a DB connection has been idle before it is considered abandoned. --> <value>60</value> </parameter> <parameter> <name>logAbandoned</name> <!-- Log a stack trace of the code which abandoned --> <value>false</value> </parameter> </ResourceParams> </Context>
注意:
所有的入口和资源都放在JNDI命名空间里的java:comp/env段里
设置JNDI资源要在$CATALINA_HOME/conf/server.xml文件里使用下列标志符:
1) <Resource>--设置应用程序可用的资源的名字和类型(同上面说的<resource-ref>等价)。
2) <ResourceParams>--设置Java资源类工厂的名称或将用的JavaBean属性。
上述这些标志符必须放在<Context>和</Context>之间
(2)、拷贝SQLServer的JDBC驱动程序到Tomcat的commonlib目录下
(3)、在程序中利用数据源来访问数据库
try { Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); DataSource ds = (DataSource)envCtx.lookup("jdbc/webmis"); Connection con=ds.getConnection(); } catch (NamingException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }
5、在server.xml文件中与数据源的描述相关的标签含义
maxActive 连接池的最大数据库连接数。设为0表示无限制。
maxIdle 数据库连接的最大空闲时间。超过此空闲时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。
maxWait 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
removeAbandoned 回收被遗弃的(一般是忘了释放的)数据库连接到连接池中。
removeAbandonedTimeout 数据库连接过多长时间不用将被视为被遗弃而收回连接池中。
logAbandoned 将被遗弃的数据库连接的回收记入日志。
driverClassName JDBC驱动程序。
url 数据库DSN连接字符串
6、在Web应用的web.xml文件中引用该资源
将下面的标签放在放在<web-app>和</web-app>中间
<!-- Database Config start --> <resource-ref> <description>connectDB test</description> <res-ref-name>jdbc/webmis</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <!-- Database Config end -->
以下是一个综合配置实例
首先在C:根目录下建立文件夹mywebapp,作为一个虚拟目录的位置。
建立一个Sql Server数据库DataBonus
找到C:jakarta-tomcat-5.0.19confserver.xml,打开。
<Context path="/mywebapp" docBase="C:/mywebapp" debug="0" reloadable="true"> <Resource name="jdbc/mybonusds" auth="Container" type="javax.sql.DataSource"/> <ResourceParams name="jdbc/mybonusds"> <parameter> <name>factory</name> <value>org.apache.commons.dbcp.BasicDataSourceFactory</value> </parameter> <parameter> <name>driverClassName</name> <value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value> </parameter> <parameter> <name>url</name> <value> jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=DataBonus </value> </parameter> <parameter> <name>username</name> <value>sa</value> </parameter> <parameter> <name>password</name> <value></value> </parameter> <parameter> <name>maxActive</name> <value>20</value> </parameter> <parameter> <name>maxIdle</name> <value>10</value> </parameter> <parameter> <name>maxWait</name> <value>-1</value> </parameter> <parameter> <name>removeAbandoned</name> <!-- Abandoned DB connections are removed and recycled --> <value>true</value> </parameter> <parameter> <name>removeAbandonedTimeout</name> <!-- Use the removeAbandonedTimeout parameter to set the number of seconds a DB connection has been idle before it is considered abandoned. --> <value>60</value> </parameter> <parameter> <name>logAbandoned</name> <!-- Log a stack trace of the code which abandoned --> <value>false</value> </parameter> </ResourceParams> </Context>
做一个JSP页面index.jsp放到mywebapp下面,代码:
<%--字符集设为"gb2312",使动态页面支持中文--%> <%@ page contentType="text/html; charset=GB2312"%> <!-- 这里使用一个字串变量 ("PAGETITLE") 保持题目和主标题的一致性。--> <html> <head> <title> <%= pagetitle %> </title> </head> <body bgcolor=#FFFFFF> <font face="Helvetica"> <h2> <font color=#DB1260> <%= pagetitle %> </font> </h2> <!-- 导入必要的类和类库 --> <%@ page import=" javax.naming.*, java.sql.*, javax.sql.DataSource "%> <!-- 声明一个类方法 --> <%! //声明变量 //标题 String pagetitle = "这是JSP调用数据库的例子"; %> <!-- 下面这些代码将被插入到servlet中 --> <% java.sql.Connection conn= null; java.sql.Statement stmt =null; java.sql.ResultSet rs=null; try { // 通过JNDI获取主接口 Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); DataSource ds = (DataSource)envCtx.lookup("jdbc/mybonusds"); conn=ds.getConnection(); stmt = conn.createStatement(); //执行SQL语句 stmt.execute("select * from 奖金"); //取得结果集 rs = stmt.getResultSet(); %> <table border="1"> <tr> <td width="60" height="20"><% out.print("编号"); %></td> <td width="80" height="20"><% out.print("姓名"); %></td> <td width="200" height="20"><% out.print("发奖名称"); %></td> <td width="100" height="20"><% out.print("金额"); %></td> <td width="200" height="20"><% out.print("备注"); %></td> </tr> <% while (rs.next()) { %> <tr> <td width="60" height="20"><% out.print(rs.getString("编号")); %></td> <td width="80" height="20"><% out.print(rs.getString("姓名")); %></td> <td width="200" height="20"><% out.print(rs.getString("发奖名称")); %></td> <td width="100" height="20"><% out.print(rs.getString("金额")); %></td> <td width="200" height="20"><% out.print(rs.getString("备注")); %></td> </tr> <% } %> </table> <% // Catch exceptions } catch (Exception e) { } finally { if (rs != null) { try{rs.close();}catch(Exception ignore){}; } if (stmt != null) { try{stmt.close();}catch(Exception ignore){}; } if (conn != null) { try{conn.close();}catch(Exception ignore){}; } %> <% } %> </font> </body> </html>
启动Tomcat。
浏览:http://127.0.0.1:8080/mywebapp/index.jsp