好学IT学院:IT信息技术分享交流平台
学段:职业中期  学科:软件工程  标签:Java  来源:站长天空  作者:本站整理  发布时间:2010-03-24  ★★★加入收藏〗〖手机版
摘要:本文阐述了怎么使用dbms存储过程。我阐述了使用存储过程的基本的和高级特性,比如返回resultset。本文假设你对dbms和jdbc已经非常熟悉,也假设你能够毫无障碍地阅读其它语言写成的代码(即不是java的语言),但是,并不要求你有任何存储过程的编程经历。…

复杂的返回值

关于存储过程的知识,很多人好像就熟悉我们所讨论的这些。如果这是存储过程的全部功能,那么存储过程就不是其它远程执行机制的替换方案了。存储过程的功能比这强大得多。

当你执行一个sql查询时,dbms创建一个叫做cursor(游标)的数据库对象,用于在返回结果中迭代每一行。resultset是当前时间点的游标的一个表示。这就是为什么没有缓存或者特定数据库的支持,你只能在resultset中向前移动。

某些dbms允许从存储过程中返回游标的一个引用。jdbc并不支持这个功能,但是oracle、postgresql和db2的jdbc驱动器都支持在resultset上打开到游标的指针(pointer)。

设想列出所有没有活到退休年龄的诗人,下面是完成这个功能的存储过程,返回一个打开的游标,同样也使用postgresql的pl/pgsql语言:

create procedure list_early_deaths () return refcursor as declare
toesup refcursor;
begin
open toesup for select poets.name, deaths.age from poets, deaths -- all entries in deaths are for poets. -- but the table might become generic.
where poets.id = deaths.mort_id and deaths.age < 60;
return toesup;
end; language pl/pgsql;

下面是调用该存储过程的java方法,将结果输出到printwriter:

printwriter:

static void sendearlydeaths(printwriter out){
connection con = null;
callablestatement toesup = null;
try {
con = connectionpool.getconnection();
// postgresql needs a transaction to do this... con.
setautocommit(false); // setup the call.
callablestatement toesup = connection.preparecall("{ ? = call list_early_deaths () }");
toesup.registeroutparameter(1, types.other);
toesup.execute();
resultset rs = (resultset) toesup.getobject(1);
while (rs.next()) {
string name = rs.getstring(1);
int age = rs.getint(2);
out.println(name + " was " + age + " years old.");
}
rs.close();
}
catch (sqlexception e) { // we should protect these calls. toesup.close(); con.close();
}
}

因为jdbc并不直接支持从存储过程中返回游标,我们使用types.other来指示存储过程的返回类型,然后调用getobject()方法并对返回值进行强制类型转换。

这个调用存储过程的java方法是mapping的一个好例子。mapping是对一个集上的操作进行抽象的方法。不是在这个过程上返回一个集,我们可以把操作传送进去执行。本例中,操作就是把resultset打印到一个输出流。这是一个值得举例的很常用的例子,下面是调用同一个存储过程的另外一个方法实现:

public class processpoetdeaths{
public abstract void senddeath(string name, int age);
}
static void mapearlydeaths(processpoetdeaths mapper){
connection con = null;
callablestatement toesup = null;
try {
con = connectionpool.getconnection();
con.setautocommit(false);
callablestatement toesup = connection.preparecall("{ ? = call list_early_deaths () }");
toesup.registeroutparameter(1, types.other);
toesup.execute();
resultset rs = (resultset) toesup.getobject(1);
while (rs.next()) {
string name = rs.getstring(1);
int age = rs.getint(2);
mapper.senddeath(name, age);
}
rs.close();
} catch (sqlexception e) { // we should protect these calls. toesup.close();
con.close();
}
}

这允许在resultset数据上执行任意的处理,而不需要改变或者复制获取resultset的方法:

static void sendearlydeaths(final printwriter out){
processpoetdeaths mymapper = new processpoetdeaths() {
public void senddeath(string name, int age) {
out.println(name + " was " + age + " years old.");
}
};
mapearlydeaths(mymapper);
}

这个方法使用processpoetdeaths的一个匿名实例调用mapearlydeaths。该实例拥有senddeath方法的一个实现,和我们上面的例子一样的方式把结果写入到输出流。当然,这个技巧并不是存储过程特有的,但是和存储过程中返回的resultset结合使用,是一个非常强大的工具。

结论

存储过程可以帮助你在代码中分离逻辑,这基本上总是有益的。这个分离的好处有:

◆ 快速创建应用,使用和应用一起改变和改善的数据库模式。
  ◆ 数据库模式可以在以后改变而不影响java对象,当我们完成应用后,可以重新设计更好的模式。
  ◆ 存储过程通过更好的sql嵌入使得复杂的sql更容易理解。
  ◆ 编写存储过程比在java中编写嵌入的sql拥有更好的工具--大部分编辑器都提供语法高亮!
  ◆ 存储过程可以在任何sql命令行中测试,这使得调试更加容易。

并不是所有的数据库都支持存储过程,但是存在许多很棒的实现,包括免费/开源的和非免费的,所以移植并不是一个问题。oracle、postgresql和db2都有类似的存储过程语言,并且有在线的社区很好地支持。

存储过程工具很多,有像toad或tora这样的编辑器、调试器和ide,提供了编写、维护pl/sql或pl/pgsql的强大的环境。

存储过程确实增加了你的代码的开销,但是它们和大多数的应用服务器相比,开销小得多。如果你的代码复杂到需要使用dbms,我建议整个采用存储过程的方式。

成功网络广告设计的8项原则
  现在乃至将来都是一个过剩的消费时代。在一个相对富裕的社会里,消费者的消费目的,不只是为了需要而消费,更多地是为消费而消费,为感觉而…
  • 好学触屏公众号虎力全开、杨帆起航!
  • 好学考试H5触屏版开放内测