JDBC是由一系列连接(Connection)、SQL语句(Statement)和结果集(ResultSet)构成的,其主要作用概括起来有如下3个方面:
建立与数据库的连接。
向数据库发起查询请求。
处理数据库返回结果。
这些作用是通过一系列API实现的,其中的几个重要接口如表13-1所示。
<ccid_nobr>
<ccid_code>表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所示。
对于简单的应用程序,程序开发人员需要在此类中直接使用的惟一方法是 DriverManager.getConnection。该方法是用来建立与数据库的连接的。JDBC 允许用户调用 DriverManager 的方法 getDriver、getDrivers 和 registerDriver 及 Driver 的方法 connect。但多数情况下,最好让 DriverManager 类管理建立连接的细节。
Connection
Connection是用来表示数据库连接的对象,对数据库的一切操作都是在这个连接的基础上进行的。Connection类的主要成员方法如表13-3所示。
<ccid_nobr>
<ccid_code>表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所示。
<ccid_nobr>
<ccid_code>表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所示。
<ccid_nobr>
<ccid_code>表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点奖励,则可以用下面的方法设置空字段的内容:
<ccid_nobr>
<ccid_code>pstmt.setInt(1, 5000); pstmt. setInt (2, 1); |
如果我们想给前50个注册的用户每人5000点奖励,则可以用循环语句对空字段进行设置:
<ccid_nobr>
<ccid_code>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 参数发送文件内容的例子:
<ccid_nobr>
<ccid_code>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所示。
<ccid_nobr>
<ccid_code>表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) 注册输出参数 调用存储过程的语法为: {call procedure_name} // 过程不需要参数 {call procedure_name[(?,?,?,…)]} // 过程需要若干个参数 {? = call procedure_name[(?,?,?,…)]} //过程需要若干个参数并返回一个参数 |
其中procedure_name为存储过程的名字,方括号中的内容是可选的多个用于存储过程执行的参数。CallableStatement 对象的创建方法如下:
CallableStatement cstmt = con.prepareCall("{call getData(?, ?)}");
向存储过程传递执行需要参数的方法是通过setXXX语句完成的。例如,我们可以将两个参数设置如下:
<ccid_nobr>
<ccid_code>cstmt.setByte(1, 25); cstmt.setInt(2,64.85); |
如果需要存储过程返回运行结果,则需要调用registerOutParameter方法设置存储过程的输出参数,然后调用getXXX方法来获取存储过程的执行结果。例如:
<ccid_nobr>
<ccid_code>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所示。
<ccid_nobr>
<ccid_code>表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所示。
<ccid_nobr>
<ccid_code>表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 指针向下移动一行。(T111)
本文选自飞思图书《精通Java核心技术》