文章转自:http://yulj.bokee.com/323699.html
企业应用一般都需要连接数据库,自从有了应用服务器之后,应用连接数据库就可以利用数据库连接池的方式来提高性能。基于J2EE的WEB应用一般是通过JDBC的方式来访问数据库,而且大部分都是通过数据源的方式来得到数据库连接的。WebSphere应用服务器(WAS)自从3.5版本便开始支持JDBC2.0,并支持用JNDI的方式来查找数据源然后生成数据库连接。我们在工作的过程中发现有相当多的用户在配置和使用数据源时曾遇到各种各样的问题。本文将介绍WAS 5.x版本中数据源的配置和使用方法,并结合实际客户遇到的常见问题进行一些讨论,以使更多用户能在WAS上顺利的配置和使用数据源。
企业应用一般都需要连接数据库,自从有了应用服务器之后,应用连接数据库就可以利用数据库连接池的方式来提高性能。基于J2EE的WEB应用一般是通过JDBC的方式来访问数据库,而且大部分都是通过数据源的方式来得到数据库连接的。WebSphere应用服务器(WAS)自从3.5版本便开始支持JDBC2.0,并支持用JNDI的方式来查找数据源然后生成数据库连接。我们在工作的过程中发现有相当多的用户在配置和使用数据源时曾遇到各种各样的问题。本文将介绍WAS 5.x版本中数据源的配置和使用方法,并结合实际客户遇到的常见问题进行一些讨论,以使更多用户能在WAS上顺利的配置和使用数据源。
1.简单原理和背景介绍
1.1 什么是数据源?为什么要使用数据源?
我们先来看一下使用JDBC1.0的时候,一般是怎样来连接数据库的。
代码片断示例1:
java.sql.Connection con = null;
try {
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");
con = java.sql.DriverManager.getConnection("jdbc:db2:DBName");
} catch (Exception e) {
System.err.println("Exception: " + e.getMessage());
}
由于建立一个数据库连接是一种比较耗时的操作,在使用JDBC1.0的时候,如果要提高性能就需要应用程序自己对连接进行缓存处理。但是在JDBC2.0 以一个附加的 API 的方式引入了标准连接池的特性,这个 附加的 API 叫做 JDBC 2.0 可选包(也称作 JDBC 2.0 标准扩展)。这样对于支持JDBC2.0的数据库(一般是指JDBC驱动程序支持),WebSphere应用服务器提供了连接池的支持。JDBC2.0 API 为连接池提供了一个客户端和一个服务器端的接口。 客户端接口是 javax.sql.DataSource,通常就是应用代码用来请求一个缓冲了的数据库连接的东西。这个数据源就是生成数据库连接对象的工厂。服务器接口是 javax.sql.ConnectionPoolDataSource,通常是大多数应用服务器和数据库JDBC驱动打交道的接口。
连接池可改进任何需要连接的应用程序(特别是基于 Web 的应用程序,Web 用户的连接和断开更为频繁)的响应时间。因为连接池的实现,在客户端调用 close()方法的时候实际上并不关闭连接,而是把连接返回到一个可重用连接的连接池中给其它客户端使用。这样就避免了任何重复打开和关闭数据库连接造成的开销,并且允许大量的客户端分享相对较少的数据库连接,从而提高数据库操作的性能。为了使用应用服务器提供的连接池功能,我们需要先定义相应的数据源。数据源在WebSphere应用服务器中作为资源存在,因此可以统一通过JNDI来查询它们。
使用数据源的代码片断示例2:
try{
javax.naming.InitialContext ctx = new javax.naming.InitialContext();
javax.sql.DataSource ds = (javax.sql.DataSource)ctx.lookup("jdbc/DSName");
java.sql.Connection con = ds.getConnection();
}catch(javax.naming.NamingException e1){
System.err.println("Naming-Exception: " + e1.getMessage());
}catch (java.sql.SQLException e2) {
System.err.println("Naming-Exception: " + e2.getMessage());
}
使用数据源的另一个好处是安全性的提高。在不使用数据源的情况下,我们一般通过下面的代码来连接Oracle数据库。
代码片断3:
//装载JDBC驱动程序
java.sql.DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
//设置数据库连接字符串
java.lang.String strConn = "jdbc:oracle:thin:@hostname/IP:1521:SIDName";
//连接数据库
java.sql.Connection sqlConn =
java.sql.DriverManager.getConnection(strConn,"userName","passWord");
这种方式需要把数据库的用户名和口令放在程序的源代码中。而在WebSphere应用服务器中我们可以在定义数据源的时候通过J2C认证的方式配置数据库访问别名,从而提升安全性。另外,这种方法也更灵活,如果数据库用户名或口令发生变化不需要更改应用程序代码。
1.2 WebSphere应用服务器中数据源所处的位置
很多人在刚开始配置WebSphere应用服务器数据源时对J2C认证别名很不习惯,觉得JDBC的东西怎么会与J2C扯上关系。我们知道基于J2EE的WEB应用在与后台EIS(企业信息系统)集成的过程中,根据EIS的不同主要有三种连接方式,分别是JDBC,JCA/J2C和JMS。从理论上讲,JDBC驱动程序是可能归纳成一种特殊的JCA连接器,只不过这个连接器后面恰好连接着关系型数据库。至于实际标准如何发展,让我们拭目以待。下面我们看一下WebSphere应用服务器中数据库连接及持久性管理的示意图。
2.如何在WebSphere应用服务器中配置数据源
我们首先概要的看一下在WebSphere应用服务器中配置和使用数据源所涉及的几个环节。一般的,配置数据源的基本步骤如下:
首先要确认所用的数据库是支持JDBC2.0的,并确保我们的操作系统环境设置为支持 JDBC 2.0。这是使用 WebSphere应用服务器创建的数据源所必需的。这一点在使用UDB DB2 7.2的时候尤其要注意。
要确定使用哪个数据源:数据源V4.0还是 V5.0 数据源。
数据源V4.0是使用 WebSphere 旧连接管理器体系结构的 WebSphere 4.x 中的数据源,所有的 EJB1.x 模块都必须使用此数据源。 WebSphere应用服务器5.x提供对 V4.0 的 JDBC 连接管理器模型的支持,使得 J2EE 1.2 应用程序能够不经改变地运行。WAS 5.x 也支持J2EE 1.3的应用, J2EE 1.3 应用程序内的 EJB 2.0 模块,WEB 2.3模块需要使用 V5.0的JDBC 连接管理器。
创建 JDBC 提供程序。JDBC提供程序指的是JDBC驱动程序的类型及其相应的驱动程序实现JAVA包。
创建数据源。数据源是在提供特定 JDBC 驱动程序实现类的 JDBC 提供程序下创建的。
绑定资源引用。J2EE 1.3标准推荐使用数据源的引用而不是直接使用数据源的名称。
测试连接。WebSphere应用服务器5.0.x及5.1提供了数据源配置页面上提供了测试数据源连接的测试按钮。
接下来,我们对数据源配置中的最为重要的两步,即创建 JDBC提供程序时的配置步骤和创建数据源时的配置过程进行详细描述。
2.1 配置JDBC提供程序
如果是第一次配置数据源连接,需要配置JDBC提供程序。配置JDBC提供程序的步骤如下:登录WAS管理控制台页面,单击资源 -> JDBC 提供程序 -> 选择相应的作用域 -> 新建 -> 选择相应的JDBC提供程序类型。
2.1.1 选择JDBC提供程序的作用域
指定JDBC提供程序的作用域,如下图所示。
的设置取决于实际WebSphere应用服务器的拓扑结构。一般有以下三种作用域:
单元
最常规的作用域。在"单元"作用域上定义的资源对于所有节点和服务器都是可见的,除非它们被覆盖了。要查看单元作用域中定义的资源,不要在作用域选择表单中指定服务器或节点名。
节点
多数资源类型的缺省作用域。在"节点"作用域上定义的资源覆盖任何在"单元"作用域上定义的重复项,并且对于相同节点上的所有服务器都是可见的,除非它们在该节点上的服务器作用域中被覆盖了。要查看节点作用域中定义的资源,不要指定服务器,但是要在作用域选择表单中选择节点名。
服务器
定义资源的最特定作用域。在"服务器"作用域上定义的资源覆盖任何在"单元"作用域或父"节点"作用域上定义的重复资源定义,并且它们仅对于特定服务器是可见的。要查看服务器作用域中定义的资源,在作用域选择表单中指定服务器名和节点名。
以看出上面三个作用域的范围逐渐递减,并且同名的资源以最小范围的为准。这个有点类似编程语言中变量的作用域。根据上面作用域的范围的描述,在单节点的情况下,一般选用"节点"。如果只有一个应用服务器实例也可以选择"服务器"。在多个节点构成的单元中,可以分别在每个节点上分别定义,也可以选择"单元"作用域一次性定义。
2.1.2 选择JDBC提供程序
使用下拉列表选择要创建的 JDBC 提供程序类型,如下图所示。
如果支持的 JDBC 提供程序类型列表不包含我们要使用的 JDBC 提供程序,则选择用户定义的 JDBC 提供程序(User-defined JDBC Provider)。如果数据源需要两阶段提交(2PC)的支持,那么在选择JDBC驱动程序时需要选择带XA支持的驱动程序。请查询 JDBC 提供程序供应商文档以获取特定的必需属性信息。
2.1.3 JDBC提供程序的属性
JDBC提供程序的常规属性页如下图所示:
这个页面中最为重要的设置是类路径。它是指定共同组成资源提供程序类的位置路径或 JAR 文件名的列表,也即JDBC驱动程序类包。大部分常见数据库的JDBC驱动程序都以jar文件或zip文件的形式提供。例如 DB2 7.2的JDBC驱动程序文件名为db2java.zip,Oracle 8.1.7的JDBC驱动程序文件名为classes12.zip,Sybase 12的JDBC驱动程序文件名为jconn2.jar等。在WebSphere应用服务器5.1中,类路径的缺省值反映了相应数据库更新的JDBC驱动程序,例如Oracle 9i的ojdbc14.jar。类路径缺省值中还带有一个相应的WebSphere变量,我们可以用绝对路径替换或定义此WebSphere变量的值。
实现类名指定 JDBC 驱动程序实现的 Java 类名。此类可用于上面的"类路径"描述中提到的驱动程序文件,例如 COM.ibm.db2.jdbc.DB2XADataSource。一般不需要手工填入,采用缺省值即可。但是如果前面JDBC提供程序类型选了User-defined JDBC Provider的话就需要自己修改这个值。
修改完这些属性之后按确定并保存就完成了JDBC提供程序的配置。这样我们就可以在JDBC提供程序的列表中找到我们定义的JDBC提供程序(注意选择正确的作用域并点击应用按钮)。
2.2 配置数据源
创建 JDBC 提供程序后,我们就可以创建数据源以访问后端数据存储。创建新的 V 5.0 数据源的步骤基本如下:
打开WAS5.x管理控制台。
单击资源 -> JDBC 提供程序。
选择相应的作用域选项以找要为其创建数据源的 JDBC 提供程序。
选择我们要创建数据源的 JDBC 资源提供程序,会出现此提供程序的详细信息页面。
如果我们要创建 V5.0 数据源,则单击"其它特性"中的数据源。
单击新建以显示数据源的配置页面,如下图所示。
这个页面中的几个重要的设置如下。
名称:指定当前数据源的显示名称。
JNDI名:指定当前数据源的JNDI名称。其缺省值为"jdbc/显示名称"。
容器受管持久性:指定此数据源是否用于企业 bean 的容器管理的持久性。 如果选择此复选框,则为关系资源适配器创建与此数据源相应的 CMP 连接器工厂。一般的,如果应用程序中有CMP,则需要选上此项。CMP容器生成的JNDI名称为eis/数据源名称_CMP,这个值在发布含有CMP的应用时绑定使用。
语句高速缓存大小:每个连接的空闲已准备语句(PreparedStatement)数,也即已准备语句缓存的数目,用以提供性能。如果我们将此数设置得过高,我们会缺乏资源,因为系统无法打开那么多已准备语句。一般采用缺省值即可。
组件受管认证别名:此别名用于运行时的数据库认证。如果我们的应用程序资源认证(res-auth)设置为应用程序,则在"组件管理的认证别名"中设置别名。
容器受管的认证别名:此别名用于运行时的数据库认证。如果我们的应用程序资源认证(res-auth)设置为容器,则设置"容器管理的认证别名"。
WAS5的数据源用J2C的安全认证方式,故一般不需要在应用级别设置用户名和口令,而必须定义一个J2C的认证别名。如果我们的数据库(例如 Cloudscape)不支持用户标识和密码,则不要在"组件管理的认证别名"或"容器管理的认证别名"字段中设置别名。
如果没有事先定义好J2C认证别名(通过管理控制台的安全性 -> JAAS配置 -> J2C认证数据),上面的两个下拉列表中是没有相应的J2C认证别名的。可以通过点击应用按钮,在数据源属性页面的最下面出现"相关项",如下图所示。
再点击J2C认证数据条目链接,然后点击新建按钮就可以进入到定义它的页面,如下图所示。
上述页面中的别名就是标识这个J2C认证数据条目的,也即出现在数据源定义页中容器受管的认证别名和组建受管认证别名列表中的名字。用户名和密码对应目标数据库的用户名和密码。点击确定并保存。
现在通过点击相应JDBC提供程序的数据源链接,可以看到前面定义的数据源。进入数据源的配置页面,在相应的认证别名的列表中选择刚才定义的认证别名。
选择其它属性栏目中的定制属性进入数据源的定制属性页。这个页面中要定义的属性与所选数据库类型有关。一般的,对于DB2数据库,必须的属性是databaseName,即所对应数据库名字。对于Oracle数据库,必须的属性是URL,如jdbc:oracle:thin:@localhost:1521:sample。修改完定制属性之后,点击确定并保存修改。
我们已定义和保存数据源之后,可以单击测试连接按钮以确保此数据源定义中的参数是正确的。在数据源列表页上,我们可以同时选择多个数据源并测试它们。一般配置完数据源之后不需要重新启动服务器,就可以测试成功。但是注意如果我们添加新的 WebSphere 环境变量或修改它,必须重新启动进程(网络部署环境下还要重启Node Agent 和 Deployment Manager)使这些变量生效。
至此,数据源的基本配置就完成了。但是在很多情况下我们还要对连接池的属性进行调整。通过点击数据源配置页面中其它属性的连接池链接,可以得到连接池的配置页面,如下图所示。
这个配置页面的几个重要属性说明如下:
连接超时:这个值指定当连接池达到给定连接池的最大值(最大连接数)时所等待的时间。当超过这个时间还是没有空闲连接时,连接请求超时并抛出 ConnectionWaitTimeoutException。如果连接超时设置为 0,则只要必需,池管理器就会等待直到分配一个连接为止(这在连接数下降到最大连接数值以下时发生)。
最大连接数和最小连接数:这两个参数分别指定可以在此池中创建的最大物理连接数和最小物理连接数。应用服务器启动的时候,连接池并不建立连接。只有当应用程序请求数据库连接时,连接池才开始建立连接。当连接池中的连接数达到最小连接数之后,此后根据实际应用程序对数据库连接的需求,连接池中的连接数就保持在最小连接数和最大连接数之间。可以根据应用程序对数据库连接的要求调整这两个参数。
不使用超时:这个参数指定一个空闲连接在连接池中能够存活的最大时间。因为在连接池中保持连接会消耗系统资源,因此超过最小连接数的空闲连接会被定时清除。不使用超时设为0时就不清除空闲连接。
获得时间:连接池中的连接由一个定时运行的线程进行维护。这个参数就是指定运行连接池维护线程之间的间隔。例如,如果"获得时间"设置为 60,则池维护线程每 60 秒运行一次。当池维护线程运行时,它废弃所有未使用的连接(未使用时间长于"不使用超时"中指定的时间值),直到它到达最小连接数中指定的连接数。池维护线程还废弃所有活动时间长于"时效超时"中指定的时间值的连接。获得时间间隔还影响性能,因为更短的间隔意味着池维护线程将更频繁的运行并降低性能。要禁用池维持线程,"获得时间"设置为 0,或"不使用超时"和"时效超时"都设置为 0。
时效超时:这个参数指应用在获得连接之后而不使用它的最大空闲时间。超过大概两倍时效超时,这个空闲连接将被强行放回到连接池(注:这个工作也是由连接池维护线程来做的,因为整个过程要等待两个时效超时,因此总超时时间大概是时效超时的两倍)。如果在放回到连接池之后,应用再去使用这个连接就会报StaleConnectionException异常。这个参数对事务处理中的连接不生效。时效超时设为0时这个参数就不生效。这里有一点要注意,虽然WebSphere应用服务器可以通过设置这个参数可以回收应用程序中忘记释放的数据库连接,但是在大并发量用户访问的时候还是会导致数据库连接不够用的异常。因此,尽量保证应用程序中使用完数据库连接之后及时放回到连接池中去。
上面三个参数之间是有一定联系的。不使用超时和时效超时都是由连接池维护线程来进行维护和判断的,因此真正的超时生效时间有时要比设定的大,理论上最大值为不使用超时或时效超时再加上获得时间。另外,应该将"获得时间"值设置为小于"不使用超时"和"时效超时"的值,否则后两个参数的意义就打折扣了。
清除策略:指定检测到旧文件连接或致命连接错误时指定如何清除连接,即清除策略。它有两个候选值,一个是"整个连接池",表示立即关闭任何未使用的连接。关闭在使用的连接并在该连接上进行下一个操作期间抛出 StaleConnectionException。来自应用程序的后继 getConnection 请求导致打开到数据库的新连接。使用此清除策略时,可能会不必要地关闭池中不是旧文件连接的一些连接。然而,这种情况不太可能会发生。在多数情况下,EntirePool 的清除策略是最佳选项。这也是清除策略的缺省值。另一个是"仅在连接失败时",它表示仅关闭导致 StaleConnectionException 的连接。当此设置消除不必要地关闭有效连接的可能性时,但从应用程序角度,它使恢复更为复杂。因为仅关闭了当前失败连接,极有可能使应用程序的下一个 getConnection 请求从池中返回也是旧文件的连接,导致更多旧文件连接异常。
3.常见问题讨论
问题 1
在节点作用域或单元作用域时,数据源中定义的最大连接数是指每个服务器实例的最大连接数还是总的连接数?
无论定义的资源的作用域是什么,资源的属性仅在单个服务器级别上应用。例如,如果我们在单元级别上定义数据源的作用域(它在该单元内是唯一的),则该单元中的所有用户都可以查找和使用该数据源。然而,资源属性设置对于单元中的每台服务器是本地的。例如,如果我们定义最大连接数为 10,那么该单元中的每台服务器都可以有 10 个连接。
问题 2
使用DB2 7.2与DB2 8.1在配置数据源有什么不一样的地方吗?
DB2 7.2缺省支持的JDBC级别是1.0的,因此如果需要使用数据源就必须启用JDBC2.0。而对于 DB2 V8.1缺省情况下支持 JDBC 2.0。DB2 7.2 JDBC 2.0的启用参见下面说明。
1.在 Windows NT 系统上启用 DB2 的 JDBC 2.0
要在 Windows NT 系统上启用 JDBC 2.0 使用:
停止 DB2 JDBC Applet Server 服务。
运行下列批处理文件:
SQLLIB/java12/usejdbc2.bat
2.确定 Windows NT 系统上用于 DB2 的 JDBC API 的级别
要确定系统上使用的 JDBC 级别:
如果 JDBC 2.0 在使用中,此文件存在于:
SQLLIB/java12/inuse
如果 JDBC 1.0 在使用中,则此文件存在于:
SQLLIB/java11/inuse
或不存在 java11 目录。
3.在 UNIX 系统上启用 DB2 的 JDBC 2.0
启动 WebSphere Application Server 前,调用 $INSTHOME/sqllib/java12/usejdbc2 以使用 JDBC 2.0。为了方便,我们可能要将其放置在 root 用户的启动脚本中。例如,在 AIX 上,将以下内容添加到 root 用户的 .profile:
if [ -f /usr/lpp/db2_07_01/java12/usejdbc2 ] ; then
. /usr/lpp/db2_07_01/java12/usejdbc2
fi
另外,DB2 7.2与DB2 8.1的JDBC驱动程序类包已不一样。前者只需要db2java.zip一个包,而后者需要db2jcc.jar,db2jcc_license_cu.jar,db2jcc_license_cisuz.jar三个包。
问题 3
为什么SystemOut.log日志中经常报下列信息。
[03-9-2 17:19:11:916 CST] 6b0e97e8 ConnectionFac I J2CA0122I: 无法定位资源引用 jdbc/db2ds,因此使用下列缺省值:[Resource-ref settings]
res-auth: 1 (APPLICATION)
res-isolation-level: 0 (TRANSACTION_NONE)
res-sharing-scope: true (SHAREABLE)
res-resolution-control: 999 (undefined)
J2EE 1.3的规范里面是推荐通过引用来访问各种资源,而不是直接使用资源的名字。对于数据源而言,虽然可以通过InitialContext的lookup("jdbc/DSName")来使用,但这不是推荐的做法,而且在SystemOut.log日志中出现上述信息。如果为应用程序模块定义相应的数据源引用,然后在代码中用InitialContext的lookup("java:comp/env/DSRefName")就不会出现上述信息。
问题 4
问题描述1:在AIX 5L的环境中,WAS5.01连DB2 7.2,测试连接失败。浏览器上显示的错误信息如下:
Test Connection failed for datasource DSNAME on server server1 at node NODENAME with the following exception:
java.sql.SQLException:SQLAllocEnv() Error: rc = -1. View JVM logs for further details.
在SystemOut.log日志中的异常信息如下:
[2/27/04 18:03:04:596 CST] 227c82c1 DataSourceCon E DSRA8040I: Failed to connect to the DataSource. Encountered :
java.lang.Exception: java.sql.SQLException: SQLAllocEnv() Error: rc = -1 at
com.ibm.ws.rsadapter.DSConfigurationHelper.testConnectionForGUI(DSConfigurationHelper.java:1786) at
java.lang.reflect.Method.invoke(Native Method)
问题描述2:在AIX 5L的环境中,WAS 5.1连DB2 7.2,测试连接失败。浏览器显示的错误信息如下:
测试节点 swf50 的服务器 server1 上的数据源 TestDS 失败,含有以下异常:java.lang.Exception: java.sql.SQLException:
java.lang.UnsatisfiedLinkError: Can't find library db2jdbc (libdb2jdbc.a or .so) in
sun.boot.library.path or java.library.path sun.boot.library.path=
/usr/WebSphere/AppServer51/java/jre/bin java.library.path=/usr/WebSphere/AppServer51/java/bin:...
在SystemOut.log日志中的异常信息如下:
[3/1/04 14:16:32:011 GMT+08:00] 1f05499b DataSourceCon E DSRA8040I:
Failed to connect to the DataSource. Encountered :
java.lang.Exception: java.sql.SQLException: java.lang.UnsatisfiedLinkError:
Can't find library db2jdbc (libdb2jdbc.a or .so) in
sun.boot.library.path or java.library.path
sun.boot.library.path=/usr/WebSphere/AppServer51/java/jre/bin
java.library.path=/usr/WebSphere/AppServer51/java/bin:...
(实际上,libdb2jdbc.so文件在/usr/lpp/db2_07_01/java12目录下确实存在。)
这两个问题都是WAS 5.x与DB2 7.2连接时常见的一个问题。根据出错的信息判断,应该与DB2的环境设置,或与WAS结合的环境设置有关。这个问题可以通过下面方式解决:在setupCmdLine.sh文件中或在startServer.sh中添加下面代码:
. /home/db2inst1/sqllib/db2profile
问题 5
问题描述:应用程序需要连接Oracle8.1.7,在单服务器上测试通过,在双机集群环境下配置的数据源用应用服务器提供的"测试"按钮也测试通过,但是应用发布到集群上之后,WEB模块连数据库还是报异常。
由于应用服务器中配置的数据源测试通过,因此我们主要检查应用的代码。通过检查代码发现用户应用中连接数据的代码参考了原先WebSphere应用服务器3.x连数据源的代码,主要片断如下:
public static String SYSTEM_DATASOURCE_URL =
"jdbc:oracle:thin:system/manager@192.168.0.1:1521:DBName";
public static String JNDI_PROVIDER_URL = "iiop://127.0.0.1";
public static String INITIAL_CONTEXT_FACTORY = "com.ibm.websphere.naming.WsnInitialContextFactory";
javax.sql.DataSource dataSource = null;
try {
java.util.Hashtable parms = new java.util.Hashtable();
parms.put( javax.naming.Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
parms.put( javax.naming.Context.PROVIDER_URL, JNDI_PROVIDER_URL);
javax.naming.Context context = new javax.naming.InitialContext(parms);
dataSource = (javax.sql.DataSource) context.lookup(jndiName);
在WebSphere应用服务器5.x版本,一般用缺省的InitialContext()方法即可,除非环境比较特殊,比如更改了缺省的bootstrap端口,或客户端是非WebSphere应用服务器环境等。而上述问题可以通过把代码更改为简单的InitialContext()方法即可在集群环境中正常使用。
问题 6
问题描述:应用程序在WebSphere应用服务器5上运行,数据库是Oracle 8.1.7,在JVM错误日志SystemErr.log中偶尔报下面异常信息。
4:17:27:066 GMT-08:00] 5790ff71 SystemErr R SystemBllBean.getProjTypeThrees() error!Exhausted Resultset
[2/20/04 14:19:51:145 GMT-08:00] 2bafbf43 SystemErr R Method executeQuery throws Exception!DSRA9110E: Statement is closed.
[2/20/04 14:19:51:146 GMT-08:00] 2bafbf43 SystemErr R ProjectApplySrv.getProjApplys() error !DSRA9110E: Statement is closed.
[2/20/04 14:20:47:526 GMT-08:00] 26a17f44 SystemErr R
[2/20/04 14:35:33:543 GMT-08:00] 708a7f71 SystemErr R Exhausted Resultset
[2/20/04 15:29:49:930 GMT-08:00] 78a83f4c SystemErr R SystemBllBean.getProjTypeFours() error!Exhausted Resultset
[2/20/04 15:55:43:669 GMT-08:00] 5790ff71 SystemErr R Exhausted Resultset
根据Oracle官方网站上FAQ提供的信息,Exhausted Resultset一般发生在用Oracle JDBC Thin Driver连接Oracle数据库的环境中,其主要原因是由于Statement/PreparedStatement提前关闭了,之后又去调用ResultSet。数据库连接的一般使用方法是:生成或得到Connectionà生成Statement/PrepareStatement -> executeQuery()得到ResultSet。用完之后在finally语句中依次关闭ResultSet, Statement, Connection。程序员一般都知道这个顺序,那究竟是怎么回事呢?看一下应用访问数据库的代码吧。
这个应用程序有一个专门操作数据库的JavaBean,例如DBBean其中相关的方法如下:
//得到ResultSet的方法
public ResultSet executeQuery(String sql) throws SQLException {
ResultSet rs = null;
try {
this.conn = getConnection();
PreparedStatement pstmt = this.conn.prepareStatement(sql);
rs = pstmt.executeQuery();
return rs;
} catch (SQLException e) {
System.err.println(" Method executeQuery throws Exception!" + e.getMessage());
throw e;
}
}
//关闭数据库连接的方法
public void close() throws SQLException {
try {
if (this.conn != null && !conn.isClosed()) {
this.conn.close();
}
} catch (SQLException e) {
System.err.println(" Method close throws Exception!" + e.getMessage());
throw e;
}
}
其它程序代码里面通过下面的方式来操作数据库。
strSQL = ...;
DBBean MyDBBean = new DBBean();
rs = MyDBBean .executeQuery(strSQL);
if(rs != null){
while ( rs.next()) {
... }
catch () {}
finally {
try {
if(rs != null) {rs.close();rs = null;}
MyDBBean.close();
} catch (SQLException re) {
...
}
通过分析这几段代码,我们可以发现pstmt没有显式地关闭。只是因为pstmt是executeQuery(String sql)方法的局部变量,因此在其他类调用的时候没有办法直接显示关闭。而实际上rs = MyDBBean .executeQuery(strSQL);中的方法返回时pstmt变量已经失效,只是JAVA语言并不一定马上回收它。因此当得到一个比较大的结果集的时候,上述Exhausted Resultset问题就容易出现。解决的方法是修改代码结构,根据实际情况用完之后及时关闭相应的资源。