|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
C#跟java类似,但是在跨平台方面理论上可以跨平台,实际上应用不大,执行性能优于java,跟C++基本一致,但是启动速度还是慢.代码安全,但容易性能陷阱.程序|存储历程|计划|数据|数据库 本文论述了怎样利用DBMS存储历程。论述了利用存储历程的基础的和初级特征,好比前往ResultSet。本文假定你对DBMS和JDBC已十分熟习,也假定你可以毫无停滞地浏览别的言语写成的代码(即不是Java的言语),可是,其实不请求你有任何存储历程的编程履历。
存储历程是指保留在数据库并在数据库端实行的程序。你可使用特别的语法在Java类中挪用存储历程。在挪用时,存储历程的称号及指定的参数经由过程JDBC毗连发送给DBMS,实行存储历程并经由过程毗连(假如有)前往了局。
利用存储历程具有和利用基于EJB或CORBA如许的使用服务器一样的优点。区分是存储历程能够从良多盛行的DBMS中无偿使用,而使用服务器多数十分高贵。这其实不只是允许证用度的成绩。利用使用服务器所必要消费的办理、编写代码的用度,和客户程序所增添的庞大性,都能够经由过程DBMS中的存储历程所全部地替换。
你可使用Java,Python,Perl或C编写存储历程,可是一般利用你的DBMS所指定的特定言语。Oracle利用PL/SQL,PostgreSQL利用pl/pgsql,DB2利用ProceduralSQL。这些言语都十分类似。在它们之间移植存储历程其实不比在Sun的EJB标准分歧完成版本之间移植SessionBean坚苦。而且,存储历程是为嵌进SQL所计划,这使得它们比Java或C等言语加倍友爱中央式表达数据库的机制。
由于存储历程运转在DBMS本身,这能够匡助削减使用程序中的守候工夫。不是在Java代码中实行4个或5个SQL语句,而只必要在服务器端实行1个存储历程。收集上的数据往复次数的削减能够戏剧性地优化功能。
利用存储历程
复杂的老的JDBC经由过程CallableStatement类撑持存储历程的挪用。该类实践上是PreparedStatement的一个子类。假定我们有一个poets数据库。数据库中有一个设置墨客去世岁数的存储历程。上面是对老酒鬼DylanThomas(oldsoakDylanThomas,不指定是不是有关典故、文明,请品评斧正。译注)举行挪用的具体代码:
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数据库中其实不存在“DylanThomas”)
分离SQL操纵与存储历程
映照Java对象到SQL表中的行相称复杂,可是一般必要实行几个SQL语句;多是一个SELECT查找ID,然后一个INSERT拔出指定ID的数据。在高度规格化(切合更高的范式,译注)的数据库形式中,大概必要多个表的更新,因而必要更多的语句。Java代码会很快地收缩,每个语句的收集开支也敏捷增添。
将这些SQL语句转移到一个存储过程当中将年夜年夜简化代码,仅触及一次收集挪用。一切联系关系的SQL操纵都能够在数据库外部产生。而且,存储历程言语,比方PL/SQL,同意利用SQL语法,这比Java代码加倍天然。上面是我们初期的存储历程,利用Oracle的PL/SQL言语编写:
createprocedureset_death_age(poetVARCHAR2,poet_ageNUMBER)
poet_idNUMBER;
begin
SELECTidINTOpoet_idFROMpoetsWHEREname=poet;
INSERTINTOdeaths(mort_id,age)VALUES(poet_id,poet_age);
endset_death_age;
很共同?不。我赌博你必定等候看到一个poets表上的UPDATE。这也表示了利用存储历程完成是何等简单的一件事变。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范例是甚么。你也必需调剂存储历程挪用来唆使该历程前往一个值。
上面接着下面的例子。此次我们查询DylanThomas去世时的岁数。此次的存储历程利用PostgreSQL的pl/pgsql:
createfunctionsnuffed_it_when(VARCHAR)returnsinteger
declare
poet_idNUMBER;
poet_ageNUMBER;
begin
--firstgettheidassociatedwiththepoet.
SELECTidINTOpoet_idFROMpoetsWHEREname=$1;
--getandreturntheage.
SELECTageINTOpoet_ageFROMdeathsWHEREmort_id=poet_id;
returnage;
end;
languagepl/pgsql;
别的,注重pl/pgsql参数名经由过程Unix和DOS剧本的$n语法援用。同时,也注重嵌进的正文,这是和Java代码比拟的另外一个优胜性。在Java中写如许的正文固然是能够的,可是看起来很混乱,而且和SQL语句摆脱,必需嵌进到JavaString中。
上面是挪用这个存储历程的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操纵中利用了一个毛病的范例所碰着的一样。
<P> 庞大的前往值
关于存储历程的常识,良多人仿佛就熟习我们所会商的这些。假如这是存储历程的全体功效,那末存储历程就不是别的远程实行机制的交换计划了。存储历程的功效比这壮大很多。
当你实行一个SQL查询时,DBMS创立一个叫做cursor(游标)的数据库对象,用于在前往了局中迭代每行。ResultSet是以后工夫点的游标的一个暗示。这就是为何没有缓存大概特定命据库的撑持,你只能在ResultSet中向前挪动。
某些DBMS同意从存储过程当中前往游标的一个援用。JDBC其实不撑持这个功效,可是Oracle、PostgreSQL和DB2的JDBC驱动器都撑持在ResultSet上翻开到游标的指针(pointer)。
假想列出一切没有活到退休岁数的墨客,上面是完成这个功效的存储历程,前往一个翻开的游标,一样也利用PostgreSQL的pl/pgsql言语:
createprocedurelist_early_deaths()returnrefcursoras
declare
toesuprefcursor;
begin
opentoesupfor
SELECTpoets.name,deaths.age
FROMpoets,deaths
--allentriesindeathsareforpoets.
--butthetablemightbecomegeneric.
WHEREpoets.id=deaths.mort_id
ANDdeaths.age<60;
returntoesup;
end;
languageplpgsql;
上面是挪用该存储历程的Java办法,将了局输入到PrintWriter:
PrintWriter:
staticvoidsendEarlyDeaths(PrintWriterout)
{
Connectioncon=null;
CallableStatementtoesUp=null;
try
{
con=ConnectionPool.getConnection();
//PostgreSQLneedsatransactiontodothis...
con.setAutoCommit(false);
//Setupthecall.
CallableStatementtoesUp=connection.prepareCall("{?=calllist_early_deaths()}");
toesUp.registerOutParameter(1,Types.OTHER);
getResults.execute();
ResultSetrs=(ResultSet)getResults.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);
getResults.execute();
ResultSetrs=(ResultSet)getResults.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分离利用,是一个十分壮大的工具。
结论
存储历程能够匡助你在代码平分离逻辑,这基础上老是无益的。这个分别的优点有:
・疾速创立使用,利用和使用一同改动和改良的数据库形式。
・数据库形式能够在今后改动而不影响Java对象,当我们完成使用后,能够从头计划更好的形式。
・存储历程经由过程更好的SQL嵌进使得庞大的SQL更简单了解。
・编写存储历程比在Java中编写嵌进的SQL具有更好的工具--年夜部分编纂器都供应语法高亮!
・存储历程能够在任何SQL命令行中测试,这使得调试加倍简单。
并非一切的数据库都撑持存储历程,可是存在很多很棒的完成,包含收费/开源的和非收费的,以是移植并非一个成绩。Oracle、PostgreSQL和DB2都有相似的存储历程言语,而且有在线的社区很好地撑持。
存储历程工具良多,有像TOAD或TORA如许的编纂器、调试器和IDE,供应了编写、保护PL/SQL或pl/pgsql的壮大的情况。
存储历程的确增添了你的代码的开支,可是它们和年夜多半的使用服务器比拟,开支小很多。假如你的代码庞大到必要利用DBMS,我倡议全部接纳存储历程的体例。
专门做了这个例子;而java的这个例子好像就是为了教学而写的,很多教学目的的例子是不考虑优化、性能的。 |
|