|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
JAVA是一种可以撰写跨平台应用软件的面向对象的程序设计语言,由升阳(SunMicrosystems)公司的詹姆斯·高斯林(JamesGosling)等人于1990年代初开发。程序|程序员|存储历程本文论述了怎样利用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;beginSELECTidINTOpoet_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)returnsintegerdeclarepoet_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操纵中利用了一个毛病的范例所碰着的一样。
庞大的前往值
关于存储历程的常识,良多人仿佛就熟习我们所会商的这些。假如这是存储历程的全体功效,那末存储历程就不是别的远程实行机制的交换计划了。存储历程的功效比这壮大很多。
当你实行一个SQL查询时,DBMS创立一个叫做cursor(游标)的数据库对象,用于在前往了局中迭代每行。ResultSet是以后工夫点的游标的一个暗示。这就是为何没有缓存大概特定命据库的撑持,你只能在ResultSet中向前挪动。
某些DBMS同意从存储过程当中前往游标的一个援用。JDBC其实不撑持这个功效,可是Oracle、PostgreSQL和DB2的JDBC驱动器都撑持在ResultSet上翻开到游标的指针(pointer)。
假想列出一切没有活到退休岁数的墨客,上面是完成这个功效的存储历程,前往一个翻开的游标,一样也利用PostgreSQL的pl/pgsql言语:
createprocedurelist_early_deaths()returnrefcursorasdeclaretoesuprefcursor;beginopentoesupforSELECTpoets.name,deaths.ageFROMpoets,deaths--allentriesindeathsareforpoets.--butthetablemightbecomegeneric.WHEREpoets.id=deaths.mort_idANDdeaths.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,我倡议全部接纳存储历程的体例。
net程序员的大部门代码都靠控件拖拽完成的,虽然java也有,但是无论从美观和速度上都没发和.net比。java程序员都是代码完成的,所以java程序员常戏称.net程序员是操作员,呵呵。 |
|