JDBCTM 指南:入门4 - Statement
4-Statement
本概述是从《JDBCTMDatabaseAccessfromJavaTM:ATutorialandAnnotatedReference》这本书中摘引来的。JavaSoft目前正在准备这本书。这是一本教程,同时也是JDBC的重要参考手册,它将作为Java系列的组成部份在1997年春季由Addison-Wesley出版公司出版。
4.1概述
Statement对象用于将SQL语句发送到数据库中。实际上有三种Statement对象,它们都作为在给定连接上执行SQL语句的包容器:Statement、PreparedStatement(它从Statement继承而来)和CallableStatement(它从PreparedStatement继承而来)。它们都专用于发送特定类型的SQL语句:Statement对象用于执行不带参数的简单SQL语句;PreparedStatement对象用于执行带或不带IN参数的预编译SQL语句;CallableStatement对象用于执行对数据库已存储过程的调用。
Statement接口提供了执行语句和获取结果的基本方法。PreparedStatement接口添加了处理IN参数的方法;而CallableStatement添加了处理OUT参数的方法。
4.1.1创建Statement对象
建立了到特定数据库的连接之后,就可用该连接发送SQL语句。Statement对象用Connection的方法createStatement创建,如下列代码段中所示:
Connectioncon=DriverManager.getConnection(url,"sunny","");
Statementstmt=con.createStatement();
为了执行Statement对象,被发送到数据库的SQL语句将被作为参数提供给Statement的方法:
ResultSetrs=stmt.executeQuery("SELECTa,b,cFROMTable2");
4.1.2使用Statement对象执行语句
Statement接口提供了三种执行SQL语句的方法:executeQuery、executeUpdate和execute。使用哪一个方法由SQL语句所产生的内容决定。
方法executeQuery用于产生单个结果集的语句,例如SELECT语句。
方法executeUpdate用于执行INSERT、UPDATE或DELETE语句以及SQLDDL(数据定义语言)语句,例如CREATETABLE和DROPTABLE。INSERT、UPDATE或DELETE语句的效果是修改表中零行或多行中的一列或多列。executeUpdate的返回值是一个整数,指示受影响的行数(即更新计数)。对于CREATETABLE或DROPTABLE等不操作行的语句,executeUpdate的返回值总为零。
方法execute用于执行返回多个结果集、多个更新计数或二者组合的语句。因为多数程序员不会需要该高级功能,所以本概述后面将在单独一节中对其进行介绍。
执行语句的所有方法都将关闭所调用的Statement对象的当前打开结果集(如果存在)。这意味着在重新执行Statement对象之前,需要完成对当前ResultSet对象的处理。
应注意,继承了Statement接口中所有方法的PreparedStatement接口都有自己的executeQuery、executeUpdate和execute方法。Statement对象本身不包含SQL语句,因而必须给Statement.execute方法提供SQL语句作为参数。PreparedStatement对象并不将SQL语句作为参数提供给这些方法,因为它们已经包含预编译SQL语句。CallableStatement对象继承这些方法的PreparedStatement形式。对于这些方法的PreparedStatement或CallableStatement版本,使用查询参数将抛出SQLException。
4.1.3语句完成
当连接处于自动提交模式时,其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即认为已完成。对于返回一个结果集的executeQuery方法,在检索完ResultSet对象的所有行时该语句完成。对于方法executeUpdate,当它执行时语句即完成。但在少数调用方法execute的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。
有些DBMS将已存储过程中的每条语句视为独立的语句;而另外一些则将整个过程视为一个复合语句。在启用自动提交时,这种差别就变得非常重要,因为它影响什么时候调用commit方法。在前一种情况中,每条语句单独提交;在后一种情况中,所有语句同时提交。
4.1.4关闭Statement对象
Statement对象将由Java垃圾收集程序自动关闭。而作为一种好的编程风格,应在不需要Statement对象时显式地关闭它们。这将立即释放DBMS资源,有助于避免潜在的内存问题。
4.1.5Statement对象中的SQL转义语法
Statement可包含使用SQL转义语法的SQL语句。转义语法告诉驱动程序其中的代码应该以不同方式处理。驱动程序将扫描任何转义语法,并将它转换成特定数据库可理解的代码。这使得转义语法与DBMS无关,并允许程序员使用在没有转义语法时不可用的功能。
转义子句由花括号和关键字界定:
{keyword...parameters...}
该关键字指示转义子句的类型,如下所示。
escape表示LIKE转义字符
字符“%”和“_”类似于SQLLIKE子句中的通配符(“%”匹配零个或多个字符,而“_”则匹配一个字符)。为了正确解释它们,应在其前面加上反斜杠(“\”),它是字符串中的特殊转义字符。在查询末尾包括如下语法即可指定用作转义字符的字符:
{escape´escape-character´}
例如,下列查询使用反斜杠字符作为转义字符,查找以下划线开头的标识符名:
stmt.executeQuery("SELECTnameFROMIdentifiers
WHEREIdLIKE`\_%´{escape`\´};
fn表示标量函数
几乎所有DBMS都具有标量值的数值、字符串、时间、日期、系统和转换函数。要使用这些函数,可使用如下转义语法:关键字fn后跟所需的函数名及其参数。例如,下列代码调用函数concat将两个参数连接在一起:
{fnconcat("Hot","Java")};
可用下列语法获得当前数据库用户名:
{fnuser()};
标量函数可能由语法稍有不同的DBMS支持,而它们可能不被所有驱动程序支持。各种DatabaseMetaData方法将列出所支持的函数。例如,方法getNumericFunctions返回用逗号分隔的数值函数列表,而方法getStringFunctions将返回字符串函数,等等。
驱动程序将转义函数调用映射为相应的语法,或直接实现该函数。
d、t和ts表示日期和时间文字
DBMS用于日期、时间和时间标记文字的语法各不相同。JDBC使用转义子句支持这些文字的语法的ISO标准格式。驱动程序必须将转义子句转换成DBMS表示。
例如,可用下列语法在JDBCSQL语句中指定日期:
{d`yyyy-mm-dd´}
在该语法中,yyyy为年代,mm为月份,而dd则为日期。驱动程序将用等价的特定于DBMS的表示替换这个转义子句。例如,如果´28-FEB-99´符合基本数据库的格式,则驱动程序将用它替换{d1999-02-28}。
对于TIME和TIMESTAMP也有类似的转义子句:
{t`hh:mm:ss´}
{ts`yyyy-mm-ddhh:mm:ss.f...´}
TIMESTAMP中的小数点后的秒(.f...)部分可忽略。
call或?=call表示已存储过程
如果数据库支持已存储过程,则可从JDBC中调用它们,语法为:
{callprocedure_name[(?,?,...)]}
或(其中过程返回结果参数):
{?=callprocedure_name[(?,?,...)]}
方括号指示其中的内容是可选的。它们不是语法的必要部分。
输入参数可以为文字或参数。有关详细信息,参见JDBC指南中第7节,“CallableStatement”。
可通过调用方法DatabaseMetaData.supportsStoredProcedures检查数据库是否支持已存储过程。
oj表示外部连接
外部连接的语法为
{ojouter-join}
其中outer-join形式为
tableLEFTOUTERJOIN{table/outer-join}ONsearch-condition
外部连接属于高级功能。有关它们的解释可参见SQL语法。JDBC提供了三种DatabaseMetaData方法用于确定驱动程序支持哪些外部连接类型:supportsOuterJoins、supportsFullOuterJoins和supportsLimitedOuterJoins。
方法Statement.setEscapeProcessing可打开或关闭转义处理;缺省状态为打开。当性能极为重要时,程序员可能想关闭它以减少处理时间。但通常它将出于打开状态。应注意:setEscapeProcessing不适用于PreparedStatement对象,因为在调用该语句前它就可能已被发送到数据库。有关预编译的信息,参见PreparedStatement。
4.1.6使用方法execute
execute方法应该仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。当执行某个已存储过程或动态执行未知SQL字符串(即应用程序程序员在编译时未知)时,有可能出现多个结果的情况,尽管这种情况很少见。例如,用户可能执行一个已存储过程(使用CallableStatement对象-参见第135页的CallableStatement),并且该已存储过程可执行更新,然后执行选择,再进行更新,再进行选择,等等。通常使用已存储过程的人应知道它所返回的内容。
因为方法execute处理非常规情况,所以获取其结果需要一些特殊处理并不足为怪。例如,假定已知某个过程返回两个结果集,则在使用方法execute执行该过程后,必须调用方法getResultSet获得第一个结果集,然后调用适当的getXXX方法获取其中的值。要获得第二个结果集,需要先调用getMoreResults方法,然后再调用getResultSet方法。如果已知某个过程返回两个更新计数,则首先调用方法getUpdateCount,然后调用getMoreResults,并再次调用getUpdateCount。
对于不知道返回内容,则情况更为复杂。如果结果是ResultSet对象,则方法execute返回true;如果结果是Javaint,则返回false。如果返回int,则意味着结果是更新计数或执行的语句是DDL命令。在调用方法execute之后要做的第一件事情是调用getResultSet或getUpdateCount。调用方法getResultSet可以获得两个或多个ResultSet对象中第一个对象;或调用方法getUpdateCount可以获得两个或多个更新计数中第一个更新计数的内容。
当SQL语句的结果不是结果集时,则方法getResultSet将返回null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下,判断null真正含义的唯一方法是调用方法getUpdateCount,它将返回一个整数。这个整数为调用语句所影响的行数;如果为-1则表示结果是结果集或没有结果。如果方法getResultSet已返回null(表示结果不是ResultSet对象),则返回值-1表示没有其它结果。也就是说,当下列条件为真时表示没有结果(或没有其它结果):
((stmt.getResultSet()==null)&&(stmt.getUpdateCount()==-1))
如果已经调用方法getResultSet并处理了它返回的ResultSet对象,则有必要调用方法getMoreResults以确定是否有其它结果集或更新计数。如果getMoreResults返回true,则需要再次调用getResultSet来检索下一个结果集。如上所述,如果getResultSet返回null,则需要调用getUpdateCount来检查null是表示结果为更新计数还是表示没有其它结果。
当getMoreResults返回false时,它表示该SQL语句返回一个更新计数或没有其它结果。因此需要调用方法getUpdateCount来检查它是哪一种情况。在这种情况下,当下列条件为真时表示没有其它结果:
((stmt.getMoreResults()==false)&&(stmt.getUpdateCount()==-1))
下面的代码演示了一种方法用来确认已访问调用方法execute所产生的全部结果集和更新计数:
stmt.execute(queryStringWithUnknownResults);
while(true){
introwCount=stmt.getUpdateCount();
if(rowCount>0){//它是更新计数
System.out.println("Rowschanged="+count);
stmt.getMoreResults();
continue;
}
if(rowCount==0){//DDL命令或0个更新
System.out.println("NorowschangedorstatementwasDDL
command");
stmt.getMoreResults();
continue;
}
//执行到这里,证明有一个结果集
//或没有其它结果
ResultSetrs=stmt.getResultSet;
if(rs!=null){
...//使用元数据获得关于结果集列的信息
while(rs.next()){
...//处理结果
stmt.getMoreResults();
continue;
}
break;//没有其它结果