JDBC是由一系列连接(Connection)、SQL语句(Statement)和结果集(ResultSet)构成的,其主要作用概括起来有如下3个方面:
建立与数据库的连接。
向数据库发起查询请求。
处理数据库返回结果。
这些作用是通过一系列API实现的,其中的几个重要接口如表13-1所示。
表13- 1 JDBC API中的重要接口
接 口 作 用
java . sql . DriverManager 处理驱动程序的加载和建立新数据库连接
java . sql . Connection 处理与特定数据库的连接
java . sql . Statement
在指定连接中处理SQL语句
java . sql . ResultSet
处理数据库操作结果集
这些JDBC API的组成结构如图13-2所示。
图13-2 JDBC API的组成结构
DriverManager
DriverManager类是Java.sql包中用于数据库驱动程序管理的类,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应驱动程序之间建立连接,也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。DriverManager 类直接继承自java.lang.object,其主要成员方法如表13-2所示。
表13-2 DriverManager的主要成员方法及其含义
对于简单的应用程序,程序开发人员需要在此类中直接使用的惟一方法是 DriverManager.getConnection。该方法是用来建立与数据库的连接的。JDBC 允许用户调用 DriverManager 的方法 getDriver、getDrivers 和 registerDriver 及 Driver 的方法 connect。但多数情况下,最好让 DriverManager 类管理建立连接的细节。
Connection
Connection是用来表示数据库连接的对象,对数据库的一切操作都是在这个连接的基础上进行的。Connection类的主要成员方法如表13-3所示。
表13- 3 Connection类的主要成员方法及其含义
方 法 含 义
void clearWarnings 清除连接的所有警告信息
Statement createStatement ( ) 创建一个statement对象
Statement createStatement ( int resultSetType, int resultSetConcurrency)
创建一个statement对象,它将生成具有特定类型和并发性的结果集
void commit ( ) 提交对数据库的改动并释放当前连接持有的数据库的锁
void rollback ( ) 回滚当前事务中的所有改动并释放当前连接持有的数据库的锁
String getCatalog ( ) 获取连接对象的当前目录名
boolean isClosed ( ) 判断连接是否已关闭
boolean isReadOnly ( ) 判断连接是否为只读模式
void setReadOnly ( ) 设置连接的只读模式
void close ( ) 立即释放连接对象的数据库和JDBC资源
Statement
Statement用于在已经建立的连接的基础上向数据库发送SQL语句的对象。它只是一个接口的定义,其中包括了执行SQL语句和获取返回结果的方法。实际上有3种 Statement 对象:Statement、PreparedStatement(继承自Statement )和 CallableStatement(继承自PreparedStatement)。它们都作为在给定连接上执行 SQL 语句的容器,每个都专用于发送特定类型的 SQL 语句: Statement 对象用于执行不带参数的简单
SQL 语句;PreparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL 语句;CallableStatement 对象用于执行对数据库已存储过程的调用。Statement 接口提供了执行语句和获取结果的基本方法;PreparedStatement 接口添加了处理 IN 参数的方法;而 CallableStatement 添加了处理 OUT 参数的方法。
创建statement对象的方法如下:
Statement stmt = con.createStatement();
Statement接口定义中包括的方法如表13-4所示。
表13- 4 Statement接口的主要成员方法及其含义
方 法 含 义
void addBatch ( String sql ) 在Statement语句中增加用于数据库操作的SQL批处理语句
void cancel ( ) 取消Statement中的SQL语句指定的数据库操作命令
void clearBatch ( ) 清除Statement中的SQL批处理语句
void clearWarnings ( )
清除Statement语句中的操作引起的警告
void close ( ) 关闭Statement语句指定的数据库连接
boolean execute ( String sql ) 执行SQL语句
int [ ] executeBatch ( )
执行多个SQL语句
ResultSet executeQuery ( String sql ) 进行数据库查询,返回结果集
int executeUpdate ( String sql ) 进行数据库更新
Connection getConnection ( )
获取对数据库的连接
int getFetchDirection ( )
获取从数据库表中获取行数据的方向
int getFetchSize ( ) 获取返回的数据库结果集行数
int getMaxFieldSize ( )
获取返回的数据库结果集最大字段数
int getMaxRows ( ) 获取返回的数据库结果集最大行数
boolean getMoreResults ( ) 获取Statement的下一个结果
int getQueryTimeout ( )
获取查询超时设置
ResultSet getResultSet ( ) 获取结果集
int getUpdateCount ( )
获取更新记录的数量
void setCursorName ( String name ) 设置数据库Cursor的名称
void setFetchDirection ( int dir) 设置数据库表中获取行数据的方向
void setFetchSize ( int rows)
设置返回的数据库结果集行数
void setMaxFieldSize ( int max ) 设置最大字段数
void setMaxRows ( int max )
设置最大行数
void setQueryTimeout ( int seconds) 设置查询超时时间
值得注意的是,Statement 接口提供了3种执行SQL语句的方法:executeQuery、executeUpdate和execute。使用哪一个方法由SQL语句所产生的内容决定。executeQuery方法用于产生单个结果集的SQL语句,如SELECT语句。executeUpdate方法用于执行INSERT、UPDATE、DELETE及DDL(数据定义语言)语句,例如CREATE TABLE 和 DROP TABLE。executeUpdate 的返回值是一个整数,表示它执行的SQL语句所影响的数据库中的表的行数(更新计数)。execute
方法用于执行返回多个结果集或多个更新计数的语句。
PreparedStatement接口继承了Statement接口,但PreparedStatement语句中包含了经过预编译的SQL语句,因此可以获得更高的执行效率。在PreparedStatement语句中可以包含多个用"?"代表的字段,在程序中可以利用setXXX方法设置该字段的内容,从而增强了程序设计的动态性。PreparedStatement接口的主要成员方法及其含义如表13-5所示。
表13- 5 PreparedStatement接口的主要成员方法及其含义
方 法 含 义
void addBatch ( String sql ) 在Statement语句中增加用于数据库操作的SQL批处理语句
void clearparameters ( )
清除PreparedStatement中的设置参数
ResultSet executeQuery ( String sql ) 执行SQL查询语句
ResultSetMetaData getMetaData ( ) 进行数据库查询,获取数据库元数据
void setArray ( int index, Array x) 设置为数组类型
void setAsciiStream ( int index, InputStream stream , int length ) 设置为ASCII输入流
void setBigDecimal ( int index, BigDecimal x) 设置为十进制长类型
void setBinaryStream
( int index, InputStream stream , int length ) 设置为二进制输入流
void setCharacterStream
( int index, InputStream stream , int length ) 设置为字符输入流
void setBoolean ( int index, boolean x) 设置为逻辑类型
void setByte ( int index, byte b) 设置为字节类型
void setBytes ( int byte [ ] b) 设置为字节数组类型
void setDate ( int index, Date x) 设置为日期类型
void setFloat ( int index, float x) 设置为浮点类型
void setInt ( int index, int x) 设置为整数类型
void setLong ( int index, long x) 设置为长整数类型
void setRef ( int index, int ref ) 设置为引用类型
void setShort ( int index, short x) 设置为短整数类型
void setString ( int index, String x) 设置为字符串类型
void setTime ( int index, Time x) 设置为时间类型
PreparedStatement与Statement的区别在于它构造的SQL语句不是完整的语句,而需要在程序中进行动态设置。这一方面增强了程序设计的灵活性;另一方面,由于PreparedStatement语句是经过预编译的,因此它构造的SQL语句的执行效率比较高。所以对于某些使用频繁的SQL语句,用PreparedStatement语句比用Statement具有明显的优势。
PreparedStatement对象的创建方法如下:
PreparedStatement pstmt = con.prepareStatement("update tbl_User set reward = ? where userId = ?");
在该语句中,包括两个可以进行动态设置的字段:reward和userId。
例如,我们想给第一个注册的用户5000点奖励,则可以用下面的方法设置空字段的内容:
pstmt. setInt ( 1, 5000) ;
pstmt. setInt ( 2, 1) ;
如果我们想给前50个注册的用户每人5000点奖励,则可以用循环语句对空字段进行设置:
pstmt. setInt ( 1, 5000) ;
for ( int i = 0; i < 50; i+ + )
{
pstmt. setInt ( 2, i) ;
int rowCount = pstmt. executeUpdate ( ) ;
}
如果传递的数据量很大,则可以通过将 IN 参数设置为 Java 输入流来完成。当语句执行时,JDBC驱动程序将重复调用该输入流,读取其内容并将它们当做实际参数数据传输。JDBC 提供了3种将IN参数设置为输入流的方法:setBinaryStream用于含有未说明字节的流;setAsciiStream用于含有ASCII字符的流;setUnicodeStream用于含有Unicode字符的流。这些方法比其他的setXXX方法要多一个用于指定流的总长度的参数,因为一些数据库在发送数据之前需要知道它传送的数据的大小。
下面是一个使用流作为 IN 参数发送文件内容的例子:
java . io . File file = new java . io . File ( "/tmp/data" ) ;
int fileLength = file . length ( ) ;
java . io . InputStream fin = new java . io . FileInputStream ( file ) ;
java . sql . PreparedStatement pstmt = con. prepareStatement (
"update table set stuff = ? where index = 4" ) ;
pstmt. setBinaryStream ( 1, fin, fileLength) ;
pstmt. executeUpdate ( ) ;
当语句执行时,将反复调用输入流 fin 以传递其数据。
CallableStatement 对象用于执行对数据库已存储过程的调用。在CallableStatement对象中,有一个通用的成员方法call,这个方法用于以名称的方式调用数据库中的存储过程。在数据库调用过程中,可以通过设置IN参数向调用的存储过程提供执行所需的参数。另外,在存储过程的调用中,通过OUT参数获取存储过程的执行结果。
CallableStatement 接口的主要成员方法及其含义如表13-6所示。
表13- 6 CallableStatement 接口的主要成员方法及其含义
方 法 含 义
Array getArray ( int I) 获取数组
BigDecimal getBigDecimal ( int index)
BigDecimal getBigDecimal ( int index,int scale ) 获取十进制小数
boolean getBoolean ( int index) 获取逻辑类型
byte getByte ( int index) 获取字节类型
Date getDate ( int index) Date getDate
( int index, Calendar cal) 获取日期类型
double getDouble ( int index) 获取日期类型双精度类型
float getFloat ( int index) 获取日期类型浮点类型
int getint ( int index) 获取日期类型整数类型
long getLong ( int index) 获取日期类型长整数类型
Object getObject ( int index)
Object getObject ( int index, Map map ) 获取对象类型
Ref getRef ( int I)
获取日期类型Ref类型
short getShort ( int index) 获取日期类型短整数类型
String getString ( int index) 获取日期类型字符串类型
Time getTime ( int index) Time
getTime ( int index, Calendar cal) 获取时间类型
void registerOutputParameter( int index)
void registerOutputParameter( int index, int type )
void registerOutputParameter
( int index, int type , int scale ) 注册输出参数
& nbsp; & nbsp; & nbsp; & nbsp; 调用存储过程的语法为:
{ call procedure_name} //
过程不需要参数
{ call procedure_name[ ( ? , ? , ? , …) ] } //
过程需要若干个参数
{ ? = call procedure_name[ ( ? , ? , ? , …) ] } //过程需要若干个参数并返回一个参数
其中procedure_name为存储过程的名字,方括号中的内容是可选的多个用于存储过程执行的参数。CallableStatement 对象的创建方法如下:
CallableStatement cstmt = con.prepareCall("{call getData(?, ?)}");
向存储过程传递执行需要参数的方法是通过setXXX语句完成的。例如,我们可以将两个参数设置如下:
cstmt. setByte ( 1, 25) ;
cstmt. setInt ( 2, 64. 85) ;
如果需要存储过程返回运行结果,则需要调用registerOutParameter方法设置存储过程的输出参数,然后调用getXXX方法来获取存储过程的执行结果。例如:
cstmt. registerOutParameter ( 1, java . sql . Types . TINYINT) ;
cstmt. registerOutParameter ( 1, java . sql . Types . INTEGER ) ;
cstmt. executeUpdate ( ) ;
byte a = cstmt. getByte ( 1) ;
int b = cstmt. getInt ( 2) ;
从上面的程序可以看出,Java的基本数据类型和SQL中支持的数据类型有一定的对应关系。这种对应关系如表13-7所示。
表13- 7 SQL的数据类型与Java数据类型的对应关系
SQL数据类型 Java数据类型
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java . math . BigDecimal
DECIMAL java . math . BigDecimal
BIT boolean
TINYINT byte
SMALLINT short
INTEGER int
BIGINT long
REAL float
FLOAT double
DOUBLE double
BINARY byte [ ]
VARBINARY byte [ ]
LONGVARBINARY byte [ ]
DATE java . sql . Date
TIME java . sql . Time
TIMESTAMP java . sql . Timestamp
ResultSet
结果集(ResultSet)用来暂时存放数据库查询操作获得的结果。它包含了符合 SQL 语句中条件的所有行,并且它提供了一套 get 方法对这些行中的数据进行访问。ResultSet类的主要成员方法及其含义如表13-8所示。
表13- 8 ResultSet类的主要成员方法及其含义
方 法 含 义
boolean absolute ( int row) 将指针移动到结果集对象的某一行
void afterLast ( ) 将指针移动到结果集对象的末尾
void beforeFirst ( ) 将指针移动到结果集对象的头部
boolean first ( ) 将指针移动到结果集对象的第一行
Array getArray ( int row) 获取结果集中的某一行并将其存入一个数组
boolean getBoolean ( int columnIndex) 获取当前行中某一列的值,返回一个布尔型值
byte getByte ( int columnIndex) 获取当前行中某一列的值,返回一个字节型值
short getShort ( int columnIndex) 获取当前行中某一列的值,返回一个短整型值
int getInt ( int columnIndex) 获取当前行中某一列的值,返回一个整型值
long getLong ( int columnIndex) 获取当前行中某一列的值,返回一个长整型值
double getDouble ( int columnIndex) 获取当前行中某一列的值,返回一个双精度型值
float getFloat ( int columnIndex) 获取当前行中某一列的值,返回一个浮点型值
String getString ( int columnIndex) 获取当前行中某一列的值,返回一个字符串
Date getDate ( int columnIndex) 获取当前行中某一列的值,返回一个日期型值
Object getObject ( int columnIndex) 获取当前行中某一列的值,返回一个对象
Statement getStatement ( ) 获得产生该结果集的Statement对象
URL getURL ( int columnIndex) 获取当前行中某一列的值,返回一个java. net . URL型值
boolean isBeforeFirst ( ) 判断指针是否在结果集的头部
boolean isAfterLast ( ) 判断指针是否在结果集的末尾
boolean isFirst ( ) 判断指针是否在结果集的第一行
boolean isLast ( ) 判断指针是否在结果集的最后一行
boolean last ( ) 将指针移动到结果集的最后一行
boolean next ( ) 将指针移动到当前行的下一行
boolean previous ( ) 将指针移动到当前行的前一行
表13-8中可以看出,ResultSet类不仅提供了一套用于访问数据的get方法,还提供了很多移动指针(cursor,有时也译为光标)的方法。cursor是ResultSet 维护的指向当前数据行的指针。最初它位于第一行之前,因此第一次访问结果集时通常调用 next方法将指针置于第一行上,使它成为当前行。随后每次调用 next 指针向下移动一行。