db2存储过程调用java
‘壹’ 如何在DB2中执行存储过程
1、db2 create database 数据库名 <-- 创建数据库 2、db2 connect to 数据库名 user 用户名 using 用户密码 <-- 连接数据库 3、db2 -tvf otpdb_v3_db2.sql <-- 为新建数据库建立表结构 4、db2 -td@ -f 存储过程文件绝对路径 <-- 导入存储过程,无错误会提示成功 4、调用存储过程: Windows 下:db2 call 存储过程名(参数1,参数2) AIX 下:db2 <-- 要先进入DB2,方可调用存储过程或执行SQL语句 db2=>call 存储过程名(参数1,参数2) 5、验证插入数据是否成功 Windows 下:db2 select count(*) from FTOTP_USERINFO AIX 下:db2 <-- 要先进入DB2,方可调用存储过程或执行SQL语句 db2=>select count(*) from FTOTP_USERINFO Windows 下:db2 select count(*) from FTOTP_TOKENINFO AIX 下:db2 <-- 要先进入DB2,方可调用存储过程或执行SQL语句 db2=>select count(*) from FTOTP_TOKENINFO 6、db2 SELECT TOKEN, PUBKEY FROM FTOTP_TOKENINFO FETCH FIRST 10000 ROWS ONLY > 要保存文件的绝对全路径 <-- 从 DB2 中导出前一万条记录 windows - e.g. db2 SELECT TOKEN, PUBKEY FROM FTOTP_TOKENINFO FETCH FIRST 10000 ROWS ONLY > c:\abc.txt AIX - e.g. db2 SELECT TOKEN, PUBKEY FROM FTOTP_TOKENINFO FETCH FIRST 10000 ROWS ONLY > c:\abc.txt <-- 注意:不能先进入DB2,执行查询与导出命令组合 7、db2 drop procere 存储过程名 <-- 删除存储过程 8、db2 drop database 数据库名 <-- 删除指定名称的数据库 注,如果删除时提示有应用程序连接到这个数据库上,可以用如下命令断开所有应用程序的连接: db2 force application all <-- 断开所有应用程序的连接
‘贰’ 怎么使用java连接数据库
1、首先我们先建好数据库,然后建立好程序的目录,因为是适用于初学者的,所以就建立一个简单的java project,如图。
‘叁’ data studio中的存储过程怎么写。使用datastudio
简介
IBM Data Studio Developer V1.2 包含一个 Unified Debugger 客户机,支持调试 SQL 存储过程。如果您熟悉用 Eclipse 平台调试应用程序,会发现调试 DB2 SQL 过程的步骤是很相似的。在本文中,介绍如何使用 IBM Data Studio Developer V1.2 中的调试特性调试一个示例 DB2 for z/OS SQL 存储过程。本文中的许多步骤也可以应用于在 DB2 for Linux®, UNIX®, and Windows® 和 DB2 for i 上创建的 SQL 存储过程。本文中使用的示例存储过程专门针对 DB2 for z/OS v9。
要想从本文获得最大收益,您应该熟悉使用 IBM Data Studio Developer 创建数据库连接以及操作表和存储过程等数据库对象。熟悉 IBM Data Studio Developer 的好方法是下载试用版并阅读它的教程系列(见 参考资料)。
了解不同 DB2 版本中的调试器技术
DB2 for z/OS 有两种 IBM 调试器技术:
老式技术称为 SQL Debugger,支持 DB2 for z/OS V8(包含与 LUW V8 FP7 同时发布的 V8 PTF UK03933)。它只为外部 SQL 存储过程提供调试器支持。
Unified Debugger 是最新的调试器技术,支持 DB2 for z/OS V8(包含与 DB2 for Linux, UNIX, and Windows V9 FP2 同时发布的 PTF UK25860)和 DB2 for z/OS V9。对于 DB2 for z/OS V8,可以使用 Unified Debugger 调试外部 SQL 存储过程。但是,对于 DB2 for z/OS V9,它支持调试 Java 存储过程、原生 SQL 过程和外部 SQL 存储过程。
这两种技术之间的主要差异是,Unified Debugger 同时支持调试 Java 和 SQL 过程,而且继续支持调试嵌套的过程调用。本文讨论 Unified Debugger。
准备示例表
本文中创建的存储过程是用原生 SQL 编写的,以商品 ID 作为输入,使用 PRODUCT 表中存储的成本、送货费用和客户付款信息计算商品的利润。存储过程的输出称为 PROFIT,这一数据被存储回 PRODUCT 表。
清单 1 给出创建和填充 PRODUCT 表所需的 DDL。如果希望检验过程中的 SQL 语句,需要先运行这个 DDL。
清单 1. 创建示例 proct 表的 DDL
CREATE TABLE ADMF001.PRODUCT
(
ITEM VARCHAR(6) NOT NULL,
COST DECIMAL(8,2),
SHIPPING DECIMAL(8,2),
PAYMENT DECIMAL(8,2),
PROFIT DECIMAL(8,2)
);
INSERT INTO ADMF001.PRODUCT (ITEM, COST, SHIPPING, PAYMENT)
VALUES ('001', 100.00, 19.99, 150);
INSERT INTO ADMF001.PRODUCT (ITEM, COST, SHIPPING, PAYMENT)
VALUES ('002',80, 8.99, 99.99);
创建和部署存储过程
在本节中,使用 Stored Procere 向导生成示例存储过程的简单版本。需要修改生成的存储过程,添加计算利润的逻辑。然后,学习如何指定在启用调试选项的情况下部署存储过程。如果不这样做,就无法调试存储过程。
创建过程
调试现有的存储过程
如果在服务器上有任何现有的 SQL 存储过程,那么可以把它从 Database Explorer 拖入数据开发项目中,然后进行调试。但是,需要确保存储过程为调试做好了准备,这意味着存储过程在部署时要启用调试选项。
如果存储过程在部署时没有 启用调试选项,就无法调试它;必须选择 Enable debugging 并再次部署它。
按照以下步骤创建名为 PROFITUPDATE 的示例存储过程:
首先,在 Data Studio Developer 中使用 DB2 for z/OS 数据库连接创建一个新的数据开发项目。选择 File > New > Data Development Project。向导要求指定一个数据库连接。如果还没有数据库连接,那么可以使用数据库连接向导创建一个 DB2 for z/OS 数据库连接。
图 1. 选择数据库连接
接下来,使用 New Stored Procere 向导创建一个 SQL 存储过程。可以右键单击刚才创建的项目,选择 New > Stored Procere(见图 2),从而启动 New Stored Procere 向导。把存储过程命名为 PROFITUPDATE 并指定原生 SQL 作为它的语言。
图 2. 创建新的 SQL 存储过程
在 SQL Statements 页面上,在 Statement details 框中添加清单 2 所示的 SQL 语句。注意,变量名前面有一个冒号 (:),这把它定义为主机变量。
清单 2. PROFITUPDATE 存储过程的 SQL 语句
SELECT COST, SHIPPING, PAYMENT FROM PRODUCT
WHERE ITEM = :itemID;
图 3. SQL Statements 页面
单击 Validate 按钮检查 SQL 语法。如果语法有错误,它会在把语句写入过程之前指出错误。另外,它还帮助格式化 SQL 语句。图 3 给出检查之后语句的样子。单击 Next。
在 Parameters 页面上(图 4),会注意到已经基于主机变量 :itemID 创建了输入变量 itemID。在这个页面上,可以修改现有的参数、添加新参数和删除不再需要的参数。
图 4. Parameters 页面
现在,必须通过单击 Add… 按钮添加第二个参数(输出参数)。这个参数名为 itemProfit,模式为 OUT。类型为 Decimal (8,2),见图 5。单击 OK。
图 5. 添加参数
在向导的 Deploy Options 页面上,可以通过选中 Enable debugging 复选框启用调试,见图 6。单击 Finish。
图 6. 确保选中 Enable debugging 复选框
现在,应该会在图 6-1 所示的编辑器中打开创建的存储过程。
图 6-1. 生成的过程
在查看源代码时,会注意到它不包含计算利润的逻辑。需要修改存储过程的过程体,添加这种逻辑。为了节省时间,可以复制并粘贴清单 3 所示的示例过程体。
清单 3. 计算利润的存储过程
CREATE PROCEDURE PROFITUPDATE ( IN itemID VARCHAR(6),
OUT itemProfit DECIMAL(8, 2) )
VERSION VERSION1
ISOLATION LEVEL CS
LANGUAGE SQL
ALLOW DEBUG MODE
WLM ENVIRONMENT FOR DEBUG MODE WLMENV1
QUALIFIER ADMF001
COMMIT ON RETURN YES
------------------------------------------------------------------------
-- SQL Stored Procere
-- itemID
-- itemProfit
------------------------------------------------------------------------
P1: BEGIN
-- Declare variables
DECLARE itemPayment DECIMAL(8,2) DEFAULT 0.0;
DECLARE total_cost DECIMAL(8,2) DEFAULT 0.0;
P2: BEGIN
DECLARE itemCost DECIMAL(8,2) DEFAULT 0.0;
DECLARE itemShippingFee DECIMAL(8,2) DEFAULT 0.0;
-- Declare cursor
DECLARE cursor1 CURSOR FOR
SELECT COST, SHIPPING, PAYMENT FROM PRODUCT
WHERE ITEM = itemID;
OPEN cursor1;
FETCH cursor1 INTO itemCost, itemShippingFee, itemPayment;
SET total_cost = itemCost + itemShippingFee;
END P2;
SET itemProfit = itemPayment - total_cost;
UPDATE PRODUCT SET PROFIT = itemProfit
WHERE ITEM = itemID;
END P1
单击 Save 保存存储过程。现在要部署存储过程了。
部署存储过程
为了部署存储过程,在 Data Project Explorer 中右键单击过程并选择 Deploy。因为前面在创建这个过程时启用了调试,它在部署时会启用调试选项。如果还没有启用调试选项,仍然可以在部署向导的 Routine Options 部分中选中 Enable debugging 复选框(图 7)。
图 7. Deploy Routines 向导中的 Enable debugging 选项
设置调试器会话管理程序
在调试存储过程之前,了解调试器框架会有帮助。Unified Debugger 有三个组件:服务器、客户机和会话管理程序,见图 8。在这个框架中,客户机与会话管理程序通信,会话管理程序与服务器通信,从而交换调试信息和请求。
图 8. Unified Debugger 框架
可以看出会话管理程序非常重要,它是一个中间件,负责通过 TCP/IP 连接协调客户机和服务器之间的通信。在使用调试器之前,需要在网络上某个地方(客户机、服务器或其他地方)启动会话管理程序进程。如果还没有正确地设置它,那么由于会话管理程序不可用,在使用 IBM Data Studio Developer 时会遇到问题(图 9),因此无法使用 Unified Debugger。
图 9. 会话管理程序不可用
如果在环境中的服务器上运行会话管理程序,就不需要在客户端做任何设置。但是,服务器端设置很复杂(尤其是在 z/OS 环境中),所以本文只关注在客户端上使用调试器,服务器端设置留到另一篇文章中讨论。同时,建议通过 参考资料 中列出的 Redbook 了解为存储过程调试设置 DB2 for z/OS 环境的信息。
如果会话管理程序不在本地运行,应该怎么办?
如果会话管理程序在网络上的某个地方运行,那么仍然可以使用本节描述的步骤,但是需要找到驻留它的服务器上的 db2dbgm.bat。
使用会话管理程序的最简便方法是在客户机上运行它,然后配置 debugger Preferences 页面。在客户机上运行会话管理程序的步骤如下:
找到 Data Studio Developer v1.2 附带的会话管理程序。这个文件名为 db2dbgm.bat。它位于以下目录中:
[DS install directory] \dsdev\bin
在命令提示窗口中执行 db2dbgm.bat。它会提供一个主机 IP 地址和端口号,您应该记住它们。它的设置还包括超时信息。图 10 所示的 db2dbgm.bat 示例输出表明,如果会话管理程序空闲了 50 分钟,它就会超时。(指定零值表示没有会话管理程序超时)。可以通过编辑 db2dbgm.bat 修改空闲时间和端口号。
图 10. 启动会话管理程序并获得 IP 地址和端口号
既然已经得到了正在运行的会话管理程序的 IP 地址和端口号,就需要在调试器首选项的会话管理程序部分中输入这些信息:
选择 Window>Preferences…。
在 Run/Debug > DB2 Stored Procere Debugger 下面找到 debugger preferences 页面。
单击 debugger preferences 页面上的 Use already running session manager 单选按钮,输入前面记下的 IP 地址和端口号。
图 11. 在首选项中指定会话管理程序的 IP 地址和端口号
注意:不要关闭会话管理程序窗口,因为这会结束会话管理程序会话。因此,应该继续调试并让会话管理程序窗口在后台运行。
设置其他调试器首选项
除了会话管理程序之外,还可以在开始调试之前配置其他调试器首选项。本节简要介绍其中两个选项,见图 12。
图 12. DB2 存储过程调试器首选项
Inactivity setting:在让调试器停在一个断点上时,如果不执行任何进一步的调试操作,就认为调试是空闲的。为了避免无限期地持有服务器资源,调试器假设在经过指定的无活动时间段之后应该结束调试,让过程自动地运行完。可以在 Debugger preference 页面上指定调试器的无活动时间长度。默认值是 300 秒。
Error trace setting:为了帮助判断问题,IBM 服务代表可能要求您在服务器上启用诊断跟踪。值 2 在 DB2 服务器上启用完整的诊断跟踪。除非 IBM 服务代表明确要求启用服务器诊断跟踪,否则应该保持这个设置为 0(零)。
可选:在进行调试之前设置运行设置
最后,作为开始调试之前的最后一个可选步骤,指定应用于本次运行的设置,包括:
指定在调试之前或之后运行的某些 SQL 语句(对这些语句不进行调试)
设置一个输入参数值
指定是否把更改提交到数据库
为 DB2 for z/OS 连接设置集合 ID
示例存储过程在最后更新 PRODUCT 表。为了检查更新,可以用一个运行后语句显示对 PRODUCT 表执行 SELECT 语句的结果。本节介绍具体做法。
在 Data Project Explorer 中右键单击存储过程并选择 Run Settings,打开 Run Settings 对话框,见图 13。
图 13. Run settings 菜单
Run settings 窗口有多个选项卡。选择 After Run 选项卡并输入 select * from PRODUCT 语句,见图 14。
图 14. Run settings 对话框窗口
在运行或调试存储过程之后,会执行这个 SQL 语句。利润更新的结果会显示在输出视图中。
注意:在 Run Settings 对话框中,还可以在 Parameter Values 选项卡中指定参数值。但是,如果不在这里指定,在调试过程中仍然有机会指定参数值。
回页首
开始调试
现在,已经完成了所有设置活动,可以开始调试存储过程了。
开始调试的方法是在 Data Project Explorer 中右键单击存储过程名,然后选择 Debug…,见图 15。
图 15. 在 Data Project Explorer 中从过程启动调试器
调试器首先启动标准的 Eclipse 调试配置页面,见图 16。所有配置项都预先设置了。单击 Debug 继续。
图 16. 调试配置页面
因为示例存储过程有一个输入变量,所以调试器会打开 Specify Parameter Values 对话框,可以在其中输入参数值。如果前面在 Run Settings 对话框中输入了参数值,这个值会再次显示出来,可以检查或修改它。
图 17. Specify Parameter Values 对话框
调试在 Debug 透视图中进行。下面的窗口要求您确认切换到 Debug 透视图。单击 Yes 继续。还可以单击复选框让它记住您的决定,这样就不会再提示确认。
图 18. 切换到 Debug 透视图
现在,调试器开始工作了。在存储过程逻辑前面包含 PROCEDURE 关键字的行上总是设置一个隐含的断点。当前位置用一个箭头突出显示。可以让过程停在任何执行行上进行调试。如果设置了任何断点,还可以一直运行到下一个有效断点。
图 19. 调试器停在第一行上
回页首
调试器透视图、视图和操作
Eclipse Debug 透视图(图 20)提供几个帮助调试存储过程的视图。本节介绍这些视图。
Debug 视图
Variables 视图
Breakpoints 视图
Data Output 视图
图 20. Debug 透视图概况
Debug 视图
Debug 视图显示当前的调用堆栈。它提供存储过程名和当前行号。在 Debug 视图中,可以通过图 21 所示的工具栏执行不同的操作,包括 Step Into、Step Over、Step Return、Resume 和 Terminate:
图 21. Debug 视图及其操作
使用 Step Over 逐行调试过程。
使用 Step Into 调试嵌套的过程。如果没有嵌套的过程,那么它的作用与 Step Over 相同。
Step Return 帮助从嵌套的过程返回。如果当前不在嵌套的过程中,那么 Step Return 运行完整个过程,忽略所有断点。
当停在某一行上时,可以使用 Resume 执行到下一个有效的断点。如果后面没有有效的断点,就一直运行到过程结束并停止调试。
使用 Terminate 在任何时候停止调试。
最后,可以通过单击这个图标删除所有终止的运行:
关于嵌套过程的提示:如果有嵌套的过程调用,Debug 视图可以提供外层过程和嵌套过程的调试信息,包括每个过程中的当前行号。
图 22 给出一个调试嵌套的存储过程的示例,其中的 Procere1 调用 Procere2。在调用 procere2(var1) 的行上,如果选择 step into 操作,调试器就会进入 Procere2 内部,从第一行开始调试 Procere2。在调用堆栈上可以看到,Procere2 现在出现在 Procere1 上面。调试完 Procere2 之后,调试器回到 Procere1。
图 22. 调试嵌套的存储过程
Variables 视图
Variables 视图(图 23)显示当前可用的变量和参数的当前值。Unified Debugger 还显示 SQLCODE 和 SQLSTATE 诊断信息。
图 23. Variables 视图
在 Variables 视图中可以:
指定变量修改断点。
把所有变量值复制并粘贴到其他文件。
根据调试的需要修改变量值。可以双击变量值并输入新值,也可以右键单击变量并从弹出菜单中选择 Change Value…,见图 24。
图 24. 在 Variables 视图中查看并修改变量
不能修改诊断信息 SQLCODE 或 SQLSTATE 的值,也不能在它们上面设置变量修改断点。它们不同于一般的变量。
Breakpoints 视图
Breakpoints 视图显示已经为存储过程设置的所有断点。Breakpoints 视图提供标准的断点管理支持,包括:
启用/禁用断点
删除断点
导入/导出断点
Unified Debugger 支持两种断点:行断点和变量修改断点。在下图中,Breakpoints 视图显示这两种断点。
图 25. Breakpoints 视图
添加新断点:在编辑器中双击一行右边的空白边,就会在这一行上设置行断点。还可以右键单击编辑器的左边,然后使用出现的 Add Breakpoint 菜单设置行断点。
图 26. Add Breakpoint 菜单
如果在包含有效 SQL 过程执行语句的行上设置了断点,在调试期间调试器可以停在这一行上。某些源代码行和 SQL 过程语句不能设置断点,比如注释行或游标声明。另外,每个 SQL 过程语句只有一个为语句定义可中断点的关键字。一般情况下,有效的断点是 SQL 过程语句的第一个关键字。在为跨多个源代码行的语句设置断点时要小心。可以通过 Step Over 操作熟悉这些有效断点位置。
图 27. 行断点
可以在 Variables 视图中设置变量修改断点。在 Variables 视图中,右键单击一个变量并选择 Add Variable Change Breakpoint。调试期间,当变量值改变时,调试器会停在修改变量的行上。
图 28. 创建变量修改断点
Data Output 视图
Data Output 视图显示调试完成时过程的结果。结果与正常运行存储过程时的结果相同。它提供调试操作的状态信息、运行消息、参数值和运行结果。Data Output 视图有助于检查运行存储过程的结果。如果结果不符合预期,可以重新调试存储过程。
对于我们的示例存储过程,第二个商品(item 002)的利润是 11.00。利润值显示在 Data Output 视图的 Parameters 选项卡中的 OUT 模式参数 itemProfit 中。
图 29. Data Output 视图 - 调试操作的结果
还记得在 Run Settings 中如何设置运行后语句吗?运行后语句的执行结果也显示在 Data Output 视图中。如图 30 所示,Results 选项卡中列出了 PRODUCT 表的内容。表中 item 002 的利润值是 11.00。
图 30. Data Output 视图 – 运行后语句的执行结果
回页首
需要了解的其他问题
到目前为止,已经了解了如何为调试准备会话管理程序、如何创建存储过程、如何在启用调试选项的情况下部署它以及如何通过检查变量值和结果来调试它。本节讨论在 DB2 for z/OS 上进行调试时常常出现的一些情况。
无法找到会话管理程序
正如前面提到的,可以在客户机、服务器或网络上的其他地方设置会话管理程序。有时候,即使设置了会话管理程序,IBM Data Studio Developer 仍然报告会话管理程序不可用。
如果遇到这样的问题,而且您的会话管理程序在服务器或网络上的其他地方运行,那么可能是防火墙阻断了 TCP/IP 连接。另外,有时候是由于 IP 地址不正确。检查 TCP/IP 连接路径是否通畅的好方法是从服务器 ping 会话管理程序的 IP 地址。
超时问题
Unified Debugger 使用两个 超时设置。一个用于调试客户机;另一个用于会话管理程序。如果会话管理程序在指定的时间段内没有为调试会话服务,它就会关闭。要想再次使用会话管理程序,就必须重新启动它。调试客户机会释放一段时间内无活动的调试会话。一定要根据自己的需要调整这两个设置。
调试器菜单未启用
有时候,调试器菜单被禁用了。下面两种情况会导致这个问题:
首先,可能没有在启用调试选项的情况下部署存储过程。可以在编辑器中打开过程,然后检查是否选中了 Enable Debugging 复选框。
第二,数据库连接可能不支持调试器。正如前面提到的,对于在带 PTF UK03933 的 V8 上启动的 DB2 for z/OS,提供调试器支持。如果连接到 DB2 for z/OS V7,就无法在 Data Studio Developer V1.2 中调试存储过程。
结束语
IBM Data Studio Developer 为 SQL 存储过程提供调试特性,允许在 Eclipse 框架中按照与调试 Java 应用程序相似的方式进行调试。本文介绍了如何对 DB2 for z/OS 上的 SQL 存储过程进行调试,但是许多概念也适用于其他 DB2 服务器。
致谢
我们衷心感谢 Kathy Zeidenstein、Tina Chen 和其他审阅者为审阅本文提供帮助!
参考资料
学习
通过 RSS feed 请求本系列后续文章的通知。(了解关于 developerWorks 内容的 RSS feed 的更多信息) 。
通过 IBM Data Studio 产品专题,了解 Data Studio 产品和技术相关的资源。
查阅 IBM Data Studio 的 产品页面,了解这个产品能够给企业带来的好处。
Get Started with Data Studio Developer:这个教程概述了 Data Studio Developer。
‘肆’ 执行存储过程有多少种方法java
Java执行存储过程的方法:
简单的老的JDBC通过CallableStatement类支持存储过程的调用。该类实际上是PreparedStatement的一个子类。假设有一个poets数据库。数据库中有一个设置诗人逝世年龄的存储过程。下面是对老酒鬼Dylan Thomas(old soak Dylan Thomas,不指定是否有关典故、文化,请批评指正。译注)进行调用的详细代码:
try{
intage=39;
StringpoetName="dylanthomas";
CallableStatementproc=connection.prepareCall("{callset_death_age(?,?)}");
proc.setString(1,poetName);
proc.setInt(2,age);
cs.execute();
}catch(SQLExceptione){//....}
传给prepareCall方法的字串是存储过程调用的书写规范。它指定了存储过程的名称,?代表了需要指定的参数。
和JDBC集成是存储过程的一个很大的便利:为了从应用中调用存储过程,不需要存根(stub)类或者配置文件,除了你的DBMS的JDBC驱动程序外什么也不需要。
当这段代码执行时,数据库的存储过程就被调用。我们没有去获取结果,因为该存储过程并不返回结果。执行成功或失败将通过例外得知。失败可能意味着调用存储过程时的失败(比如提供的一个参数的类型不正确),或者一个应用程序的失败(比如抛出一个例外指示在poets数据库中并不存在“Dylan Thomas”)
结合SQL操作与存储过程
映射Java对象到SQL表中的行相当简单,但是通常需要执行几个SQL语句;可能是一个SELECT查找ID,然后一个INSERT插入指定ID的数据。在高度规格化(符合更高的范式,译注)的数据库模式中,可能需要多个表的更新,因此需要更多的语句。Java代码会很快地膨胀,每一个语句的网络开销也迅速增加。
将这些SQL语句转移到一个存储过程中将大大简化代码,仅涉及一次网络调用。所有关联的SQL操作都可以在数据库内部发生。并且,存储过程语言,例如PL/SQL,允许使用SQL语法,这比Java代码更加自然。早期的存储过程,使用Oracle的PL/SQL语言编写:
createprocereset_death_age(poetVARCHAR2,poet_ageNUMBER)
poet_idNUMBER;
beginSELECTidINTOpoet_idFROMpoetsWHEREname=poet;
INSERTINTOdeaths(mort_id,age)VALUES(poet_id,poet_age);
endset_death_age;
set_death_age几乎可以肯定是一个很烂的实现。应该在poets表中添加一列来存储逝世年龄。Java代码中并不关心数据库模式是怎么实现的,因为它仅调用存储过程。以后可以改变数据库模式以提高性能,但是不必修改代码。
下面是调用上面存储过程的Java代码:
publicstaticvoidsetDeathAge(PoetdyingBard,intage)throwsSQLException{
Connectioncon=null;
CallableStatementproc=null;
try{
con=connectionPool.getConnection();
proc=con.prepareCall("{callset_death_age(?,?)}");
proc.setString(1,dyingBard.getName());
proc.setInt(2,age);
proc.execute();
}
finally{
try{proc.close();}
catch(SQLExceptione){}
con.close();
}
}
为了确保可维护性,建议使用像这儿这样的static方法。这也使得调用存储过程的代码集中在一个简单的模版代码中。如果用到许多存储过程,就会发现仅需要拷贝、粘贴就可以创建新的方法。因为代码的模版化,甚至也可以通过脚本自动生产调用存储过程的代码。
Functions
存储过程可以有返回值,所以CallableStatement类有类似getResultSet这样的方法来获取返回值。当存储过程返回一个值时,必须使用registerOutParameter方法告诉JDBC驱动器该值的SQL类型是什么。也必须调整存储过程调用来指示该过程返回一个值。
下面接着上面的例子。这次查询Dylan Thomas逝世时的年龄。这次的存储过程使用PostgreSQL的pl/pgsql:
createfunctionsnuffed_it_when(VARCHAR)returnsinteger'declare
poet_idNUMBER;
poet_ageNUMBER;
begin
--.
SELECTidINTOpoet_idFROMpoetsWHEREname=$1;
--getandreturntheage.
SELECTageINTOpoet_ageFROMdeathsWHEREmort_id=poet_id;
returnage;
end;'language'pl/pgsql';
另外,注意pl/pgsql参数名通过Unix和DOS脚本的$n语法引用。同时,也注意嵌入的注释,这是和Java代码相比的另一个优越性。在Java中写这样的注释当然是可以的,但是看起来很凌乱,并且和SQL语句脱节,必须嵌入到Java String中。
下面是调用这个存储过程的Java代码:
connection.setAutoCommit(false);
CallableStatementproc=connection.prepareCall("{?=callsnuffed_it_when(?)}");
proc.registerOutParameter(1,Types.INTEGER);
proc.setString(2,poetName);
cs.execute();
intage=proc.getInt(2);
如果指定了错误的返回值类型会怎样?那么,当调用存储过程时将抛出一个RuntimeException,正如你在ResultSet操作中使用了一个错误的类型所碰到的一样。
复杂的返回值
如果这是存储过程的全部功能,那么存储过程就不是其它远程执行机制的替换方案了。存储过程的功能比这强大得多。
当执行一个SQL查询时,DBMS创建一个叫做cursor(游标)的数据库对象,用于在返回结果中迭代每一行。ResultSet是当前时间点的游标的一个表示。这就是为什么没有缓存或者特定数据库的支持,只能在ResultSet中向前移动。
某些DBMS允许从存储过程中返回游标的一个引用。JDBC并不支持这个功能,但是Oracle、PostgreSQL和DB2的JDBC驱动器都支持在ResultSet上打开到游标的指针(pointer)。
设想列出所有没有活到退休年龄的诗人,下面是完成这个功能的存储过程,返回一个打开的游标,同样也使用PostgreSQL的pl/pgsql语言:
createprocerelist_early_deaths()returnrefcursoras'declare
toesuprefcursor;
begin
opentoesupforSELECTpoets.name,deaths.ageFROMpoets,deaths--allentriesindeathsareforpoets.--butthetablemightbecomegeneric.
WHEREpoets.id=deaths.mort_idANDdeaths.age<60;
returntoesup;
end;'language'plpgsql';
下面是调用该存储过程的Java方法,将结果输出到PrintWriter:
PrintWriter:
staticvoidsendEarlyDeaths(PrintWriterout){
Connectioncon=null;
CallableStatementtoesUp=null;
try{
con=ConnectionPool.getConnection();
//...con.
setAutoCommit(false);//Setupthecall.
CallableStatementtoesUp=connection.prepareCall("{?=calllist_early_deaths()}");
toesUp.registerOutParameter(1,Types.OTHER);
toesUp.execute();
ResultSetrs=(ResultSet)toesUp.getObject(1);
while(rs.next()){
Stringname=rs.getString(1);
intage=rs.getInt(2);
out.println(name+"was"+age+"yearsold.");
}
rs.close();
}
catch(SQLExceptione){//Weshouldprotectthesecalls.toesUp.close();con.close();
}
}
因为JDBC并不直接支持从存储过程中返回游标,使用Types.OTHER来指示存储过程的返回类型,然后调用getObject()方法并对返回值进行强制类型转换。
这个调用存储过程的Java方法是mapping的一个好例子。Mapping是对一个集上的操作进行抽象的方法。不是在这个过程上返回一个集,可以把操作传送进去执行。本例中,操作就是把ResultSet打印到一个输出流。这是一个值得举例的很常用的例子,下面是调用同一个存储过程的另外一个方法实现:
publicclassProcessPoetDeaths{
publicabstractvoidsendDeath(Stringname,intage);
}
staticvoidmapEarlyDeaths(ProcessPoetDeathsmapper){
Connectioncon=null;
CallableStatementtoesUp=null;
try{
con=ConnectionPool.getConnection();
con.setAutoCommit(false);
CallableStatementtoesUp=connection.prepareCall("{?=calllist_early_deaths()}");
toesUp.registerOutParameter(1,Types.OTHER);
toesUp.execute();
ResultSetrs=(ResultSet)toesUp.getObject(1);
while(rs.next()){
Stringname=rs.getString(1);
intage=rs.getInt(2);
mapper.sendDeath(name,age);
}
rs.close();
}catch(SQLExceptione){//Weshouldprotectthesecalls.toesUp.close();
con.close();
}
}
这允许在ResultSet数据上执行任意的处理,而不需要改变或者复制获取ResultSet的方法:
staticvoidsendEarlyDeaths(finalPrintWriterout){
ProcessPoetDeathsmyMapper=newProcessPoetDeaths(){
publicvoidsendDeath(Stringname,intage){
out.println(name+"was"+age+"yearsold.");
}
};
mapEarlyDeaths(myMapper);
}
这个方法使用ProcessPoetDeaths的一个匿名实例调用mapEarlyDeaths。该实例拥有sendDeath方法的一个实现,和我们上面的例子一样的方式把结果写入到输出流。当然,这个技巧并不是存储过程特有的,但是和存储过程中返回的ResultSet结合使用,是一个非常强大的工具。