将存储进程封装为EJB组件的要领
副标题#e#
集成 Web 应用处事器和数据库打点 (DBMS) 技能是许多新型贸易应用的常见需求。在本文中,我们将接头该集成的一个方面:如安在会话 Enterprise JavaBeans (EJB) 组件中设计与开拓封装或挪用现有 DBMS 存储进程的要领。您应该熟悉 EJB 技能、布局化查询语言 (SQL) 和 Java 数据库毗连 (JDBC) 的根基常识,以便充实领略本文。
假如您正致力于需要会见或修改在 DMBS 中数据的 Web 应用措施开拓,那么大概已经在向基于 EJB 的设计转移。您大概会发明,通过使会话 EJB 组件操作 DBMS 存储进程,可以淘汰编码和维护事情,并大概提高数据会识趣能。
一些公司多年来一直在利用存储进程(stored procedure),很洪流平上是因为它们可以辅佐淘汰网络通信量,并提高漫衍式计较情况中的机能。凡是,这些进程包括涉及大都据库操纵的重要业务逻辑。长途应用措施挪用这些进程,在 DMBS 处事器上执行它们所包括的 SQL 语句。虽然,进程竣事时,所有功效都返回给应用措施。
这些旧有存储进程对 Web 应用凡是是有用的。与其在 EJB 组件中复制这些逻辑,为什么不将这些进程作为要领封装在会话 bean 中呢?这样可以制止 DBMS 处事器和 EJB 组件中的冗余代码 — 在思量开拓、调试和维护开销时,冗余代码将损耗开拓效率。这还大概带来提高机能的长处。挪用存储进程可以淘汰 EJB 组件原本不得不发出的 SQL 语句数量,从而淘汰与长途 DBMS 的通信开销。
入门
此刻大白为什么要从会话 bean 挪用存储进程了吧,下面我们看看如何开始。首先,需要利用适当的开拓情况,该情况应该包罗一个带有内置 EJB 支持的 Java 开拓东西,一个 Web 应用处事器和一个干系 DBMS。我的参考设置包罗 VisualAge for Java 企业版 3.0.2,WebSphere Application Server 高级版 3.0.2.1,以及 DB2 V7.1,所有这些都安装在一个 Windows NT 系统上。有关如何设置该情况以支持本文所述事情的具体信息,请参阅 "Leveraging DBMS Stored Procedures through Enterprise JavaBeans"(位于参考资料中)或参考产物手册。
有了正确的软件情况,就可以开始了。固然我们要接头的编码模式大概适合于无状态会话(stateless session) bean,但它也可利用有状态会话(stateful session) bean 组件。可是,因为无状态会话 bean 比有状态会话 bean 耗损的系统资源更少,并且涉及的代码也更少,所以凡是发起利用无状态会话 bean。
首先要思量的设计问题是如安在存储进程和 EJB 组件之间映射数据。存储进程大概需要多个输入、输出和输入/输出参数,并返回一个或多个功效集(代表数据行)。除非要对差异范例的进程利用差异的编码模式,您需要编写 EJB 组件以便处理惩罚所有这些大概性。
处理惩罚输入(可能输入/输出)参数很简朴:将存储进程需要的每个参数映射成 EJB 组件的输入参数。可是,处理惩罚存储进程的输出较量棘手。大概有多个输出参数和多个功效集要传回挪用措施,需要将这些作为一个可序列化的工具返回,以切合 EJB 类型。可以编写本身的类,使其可以将这些数据打包成一个工具,并在该工具中包罗所有必需的元数据。(该元数据将描写工具的内部布局,以便客户机知道如那里理惩罚。)但这需要大量事情。
假如正在利用 VisualAge for Java 和 WebSphere,那么,有个更好的选项:利用它们的数据会见 Bean (DAB) 库。该库包括一些提供位于根基 JDBC 之上的函数层的类。大概会发明 com.ibm.db.CallableStatement 类出格利便,因为它答允建设一个可序列化的工具,该工具包括所有从存储进程返回的输出,包罗多个功效集(假如有的话)和相关元数据。尚有一个长处是,该库设计成可以支持任何支持 JDBC 的数据源,因此,它可以使 bean“与 DBMS 无关”。有了 DAB 库,就可以用一个编码模式在会话 EJB 组件中封装任何存储进程。甚至可以在 EJB 客户机中利用一个通用的编码模式,来处理惩罚任何从封装器要领返回的功效。
回首开拓任务
我们来接头一下利用通用编码模式,来集成 EJB 组件和 DBMS 存储进程的步调:
确定要将哪个存储进程封装成 EJB 要领。假如该进程不存在,则遵循 DBMS 尺渡进程来建设和调试。
确定要利用哪个无状态会话 EJB 组件。假如该 EJB 不存在,则遵循 Java 开拓情况的尺渡进程来建设和调试。
扩展 EJB 组件的长途接口,以包罗用于封装存储进程的新要领。
扩展 EJB 组件的实现,以包罗封装存储进程的新要领的逻辑。毗连到数据库、挪用存储进程、处理惩罚所有功效集和处理惩罚所有异常是后头要办理的问题。
通过构建一个客户机应用或 Servlet,来挪用 EJB 组件封装器要领,以测试所做的事情。
#p#分页标题#e#
头两项是根基编程任务,您大概已经熟悉。按照所用产物的差异,个体步调大概会略有差异,可是大大都产物都有东西来提供辅佐。譬喻,假如正在利用 VisualAge for Java 和 DB2,则可以操作“存储进程构建器”来完成步调 1,以及操作 EJB 开拓特性来完成步调 2。本文将不会合报告头两步。可是,其余三步需要具体讲授。本文将在一个实际示例的情况下接头这些步调中的每一步。
#p#副标题#e#
欣赏应用方案
假设需要构建一个应用,该应用支持一家公司的市场营销分部,该分部维护面向金融的 Web 站点。该站点答允人们注册为客户,跟踪他们的投资总额,以及在电子通告板上颁发意见。别的还假设,支持该站点的数据存储在 DB2 的表中。以下代码样本显示如何建设这些表。
在 DB2 中建设样本表的 SQL 语句
create table client (
id int not null primary key,
name varchar(30),
email varchar(30),
phone varchar(12),
regdate date,
mktg char,
constraint check1 check (mktg in (‘y‘, ‘Y‘, ‘n‘, ‘N‘))
)
create table portfolio (
id int not null,
clientID int not null references client,
ticker varchar(10) not null,
cost decimal (9,2),
qty int,
date date,
primary key (id, clientID, ticker)
)
create table boards (
msgno varchar(15) not null primary key,
subject varchar(40),
date date,
clientID int not null references client
)
该数据库还包括一个出格重要的存储进程。进程 CLIENTREPORT 提供注册站点用户的综合提要,包罗用户投资和他们在通告板上接头的问题。这个陈诉还包罗客户名称和电子邮件地点,以便在用户提出有关有潜在代价的附加产物或处事方面的发起时,市场营销人员可以与这些用户联结。要在会话 EJB 组件中封装的就是这个进程。
因为此进程大概用多种语言(包罗 Java 编程语言)编写,这里就不显示它的完整内容。不管怎么说,源代码确实不那么重要,因为您不能假设老是可得到它们。可是,为了汇报您存储进程有什么内容,这里显示了它包罗的三个 SELECT 语句:
CLIENTREPORT 存储进程中的 SQL 语句:
select name, e-mail from client where id = ?
select id, ticker, cost, qty, date from portfolio where clientid = ?
select msgno, subject, date from boards where clientid = ?
问号暗示,该语句将依赖运行时来自挪用措施的输入,在这种环境下,挪用措施必需提供一个代表感乐趣的客户标识的有效数据值。通过这些语句,您可以猜出,存储进程将需要一个输入参数(用于客户标识),返回两个输出参数(用于客户名称和电子邮件地点),并返回两个功效集(一个包括有关客户投资总额的数据,另一个包括有关客户通告板颁发内容的数据)。
修改 EJB 组件的长途接口
此刻我们来开始 EJB 组件代码事情。
既然要使封装器要领对 EJB 组件客户机可用,我们需要扩展 bean 的长途接口。将利用一个名为 Analysis 的无状态会话,并包罗一个 lookupClient 要领,以用于存储进程封装器。该要领需要一个整数作为输入,以代表要陈诉的客户标识,它返回一个 DAB CallableStatement 工具(位于 com.ibm.db.* 包中)。将把该进程返回的任何异常转换成 RemoteExceptions(这合用于与 EJB 1.0 兼容的会话 bean)。
以下编码示例显示了 EJB 组件长途接口的修改部门。
EJB 组件长途接口
// Enterprise JavaBean Remote Interface for Analysis session bean
public interface Analysis extends javax.ejb.EJBObject {
// remote interface for our lookupClient method
com.ibm.db.CallableStatement lookupClient(java.lang.Integer clientId)
throws java.rmi.RemoteException;
. . .
}
请留意,假如利用 VisualAge EJB 组件领导,则无需对此举办手工编码。替代要领是,在 bean 的实现类中对此要领编码之后,通过菜单项来将该要领晋升(promote)到 bean 的长途接口,然后,将自动添加必须的代码。
编码存储进程封装器要领
此刻可以会合报告 bean 实现类自己,将在该实现类中包罗挪用存储进程的代码,并将其所有输出作为 com.ibm.db.CallableStatement 工具返回。包括挪用 CLIENTREPORT 存储进程的 lookupClient(…) 要领的完整实现。将在后续章节中具体讲授每个代码块(参考代码中的注释)的逻辑,以便您更好地领略如作甚本身的存储进程实现雷同的要领。
毗连到数据库
让我们更具体地查察此代码的各部门。
#p#分页标题#e#
在挪用存储进程之前,需要成立一个到 DBMS 的毗连。有两种要领做获得:利用 1.0 样式的毗连,可能利用 JDBC 2.0 样式的 DataSource。在 WebSphere 情况中,凡是选用后者,因为它提供毗连池(connection pooling),这可以更有效地利用系统资源。出于这种原因,我们的编码模式利用 DataSource。
除了确定要成立的毗连范例之外,还应该思量要将毗连逻辑放在 bean 中的什么处所。有多个选择:
直接放在封装器要领(wrapper method)中
放在私有帮助要领(helper method)中
放在 ejbCreate() 要领中(并将相应的断开逻辑放在 ejbRemove() 要领中)
这些要领的利弊超出了本文的范畴。为简朴起见,样本代码将所有毗连/断开逻辑直接放在要领中。
代码块 1 显示了在利用 VisualAge for Java 3.0.2 和 WebSphere 3.0.2.1 时,如何利用 DataSource 举办毗连。我们建设了一个散列表,在个中填充适合于 WebSphere 情况的值,然后成立一个 InitialContext。代码的以下几行操作该初始上下文和 Java 定名和目次接口 (JNDI) 处事,来得到期望的 DataSource 的索引,我们以前在 WebSphere 顶用“打点节制台”建设了该 DataSource。本例中的 DataSource 名为 LocalDB2Sample。下一步,利用该 DataSource 来得到一个毗连,并向其通报符合的数据库用户标识和口令。从毗连池得到毗连之后,可以将该信息提供应 DAB DatabaseConnection 工具,来配置它所需的毗连类型。最后,将 autoCommitMode 配置成 false,因为 EJB 组件认真处理惩罚事务打点处事。
测试时,在 VisualAge for Java WebSphere 测试情况中运行利用 DataSource 的 EJB 组件会很利便。有关如安在产物刊行版 3.0.2 中这样做的指示,请参阅 David Zimmerman 所著的 "Creating DataSources in the VisualAge for Java WebSphere Test Environment"(在参考资料中)。
挪用存储进程
成立了毗连之后,可以会合报告如何挪用存储进程了。如封装器要领编码示例中的代码块 2 所示,首先建设一个 DAB StatementMetaData 工具,该工具中有存储进程的类型。下一步,界说要执行的 SQL 语句。在这里将要挪用 CLIENTREPORT 进程,该进程需要一个输入参数(用于客户标识)和两个输出参数(用于客户名称和电子邮件地点)。下一步,将参数添加到类型中。对付每个进程参数,都指定了参数名,其数据范例及其参数模式。
代码块 3 建设即将执行的 DAB CallableStatement 工具。CallableStatement 代表可用来执行存储进程的 SQL。建设落成具之后,将其元数据配置成在代码块 2 中指定的形式。然后将 DatabaseConnection(在代码块 1 中建设)与该 CallableStatement 关联。
下一个任务很简朴:需要执行 CallableStatement 工具,这将使 DBMS 运行存储进程。可是,在这样做之前,必需通过 EJB 客户机应用措施,将进程的输入参数配置成传入要领的值。代码块 4 中显示了这种逻辑。
检索存储进程的输出并返回到挪用措施
在封装器要领编码示例的代码块 5 中,将检索存储进程返回的输出参数。想起来了吗?这些参数代表 Web 站点客户的名称和电子邮件地点。可是,不需要显式地检索存储进程返回的功效集。(这些功效集包括有关客户投资总额和通告板颁发信息的数据)。您大概要问:为什么会这样呢?
某些 DBMS 要求,在得到任何输出参数值之前,要从存储进程返回的功效集检索所有需要的值。由于这种要求,在通过 getParameter() 要领举办出格请求之前,CallableStatement bean 不从数据库得到任何输出参数,因为何时从功效集检索数据是由用户节制的。缺省环境下,在执行存储进程之后,将自动检索功效集,并将其存储在高速缓存中。可是,必需显式检索输出参数,并将其存储在高速缓存。
检索完输出参数之后,将 DAB CallableStatement 返回给 EJB 组件的挪用措施。该工具此刻包括进程返回的所有输出(包罗功效集),和辅佐挪用措施正确阐明工具语法的适当的元数据。当我们查察挪用会话 bean 封装器要领的样本客户机应用措施时,将看到如何去做。
假如您熟悉 JDBC,大概会问:为什么不在此代码块中显式发出 commit 语句。确实,假如利用的是 JDBC 1.0 样式的毗连,大概需要(不然,当在 "finally" 块中封锁数据库毗连时,将逆序规复所做的事情)。可是,利用 DataSource 并接管 WebSphere 缺省的 EJB 组件属性 (TX_REQUIRED),WebSphere 将自动为我们的事情提供事务打点。因此,不再需要显式的 commit 语句。
处理惩罚异常与封锁打开的资源
#p#分页标题#e#
虽然,在执行会话 bean 时大概会堕落。因此,需要提供异常处理惩罚。代码块 6 包罗适合于与 EJB 1.0 兼容的 bean 的简朴异常处理惩罚措施。它只是捕捉碰着的任何异常,包罗一个适当的错误动静,并将异常作为新的 RemoteException 抛回给挪用措施。
别的,该代码块还包括一个 "finally" 块,以确保封锁所有打开的资源。在这里,释放任何与 CallableStatement 工具关联的资源。下一步,撤除在事情中所用的任何对毗连的 DAB 引用。最后,确保封锁 WebSphere 毗连。
构建客户机应用措施
构建了 EJB 封装器要领之后,该会合报告客户机应用措施了。与 EJB 组件一样,首先展示客户机应用措施的完整代码样本。然后,将具体报告个体代码块。
这里显示的客户机应用措施 — ClientAnalysis — 利用 RMI/IIOP 与 EJB 组件通信。其事情很简朴:建设会话 bean,挪用它的 lookupClient(…) 要领,处理惩罚该要领返回的 DAB CallableStatement 工具,然后撤除 bean。将该应用措施编写成处理惩罚 CallableStatement 的通用客户机,即,假设事先不知道有关 CallableStatement 内部布局的任何信息。相反,我们严格依赖个中包括的元数据,来阐明工具的语法,并利用其相关组件,如进程返回的输出参数和功效集。这种要领演示了通用的编码模式,可以在处理惩罚 CallableStatement 的任何应用措施中利用。就这样,它增补了在无状态会话 EJB 组件中对封装存储进程所用的通用编码模式。
建设 EJB 组件并挪用其封装器要领
客户机应用措施的代码块 1 以 main(…) 要领开始。它指定感乐趣的客户标识,并挪用一个私有帮助要领,来得到正在利用的会话 EJB 组件。执行完 bean 之后,挪用 lookupClient(…) 要领。这是封装 CLIENTREPORT 存储进程并返回 DAB CallableStatement 的要领。
需要具体报告私有帮助要领 — createEJB()。因为 EJB 组件建设事情大概会按照所用的 Web 应用措施而略有差异,所以,选择将这个事情断绝成单独的要领。出格是,由于与该上下文相关的特定属性将会改变,所以,得到 JNDI InitialContext 的要领大概差异。
该 createEJB() 要领建设一个散列表,然后用适合于软件情况的值填充。下一步,建设一个新的 InitialContext 工具,该工具用于通过 JNDI 处事得到对 EJB 组件的长途引用。因为从 JNDI 上下文返回 JNDI(这是在 IIOP 之上利用 RMI 的编码需求),所以,限制了该长途引用。得到 EJB 组件宿主之后,建设一个无状态会话 bean,然后将其返回给客户机应用措施的 main 要领。
处理惩罚返回的工具
客户机应用措施的代码块 2 处理惩罚 EJB 组件返回的 DAB CallableStatement 工具。首先定位与 CallableStatement 关联的根元数据工具。因为 CallableStatements 可以得到多个功效集,所以,多个 StatementMetaData 工具可以链接在一起,并包罗在 CallableStatement 中。而链的根总包括描写 SQL 语句的元数据和相关参数,因此,这就是我们的开始之处。这答允我们得到 CallableStatement 中包罗的参数数目。返回的数目将包罗进程的所有 IN、INOUT 和 OUT 参数。通过利用轮回,可以处理惩罚所有参数并打印每个参数的相关信息,包罗参数名、相应的 Java 类和模式(指明 IN、INOUT 或 OUT 模式的数字)。
下一步,查察功效集并处理惩罚它们。首先,确定 CallableStatement 工具中包罗的功效集数目。通过利用轮回,可以得到每个用 DAB SelectResult 工具暗示的功效集。然后,利用另一个私有帮助要领 processRS(…) 来处理惩罚功效集。processRS(…) 要领确定通报给 SelectResult 并包括在个中的行和列的数目。假设有一些行存在,它利用嵌套轮回来打印有关所有行中的所有列的信息。该信息包摆列名和它的值。
今朝为止,客户机应用措施的事情险些完成。代码块 3 撤除会话 bean,打印一行表白已完成,然后终止。虽然,在代码块 3 之后的代码处理惩罚任何碰着的异常。在这里,只打印一个仓库跟踪。
总结
但愿您已领略会话 EJB 组件如何操作封装在旧有 DBMS 存储进程中的贸易逻辑。这样做的其它大概的长处包罗:淘汰 EJB 处事器和 DBMS 之间的网络通信量,提跨越产力,以及低落总体软件维护本钱。假如遵循本文中列出的编码模式,则无论与进程相关的参数或功效集如何,您都可以将任何范例的存储进程作为要领封装在无状态会话 bean 中。并且,您将可以利用通用编码模式来挪用任何这样的 EJB 组件,并处理惩罚它返回的工具,而不必事先知道该工具的内部布局。