一、JDBC 简介
什么是 JDBC?
JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库。
JDBC API 库包含下面提到的每个任务,都是与数据库相关的常用用法。
- 制作到数据库的连接。
- 创建 SQL 语句。
- 执行 SQL查询数据库。
- 查看和修改所产生的记录。
从根本上来说,JDBC 是一种规范,它提供了一套完整的接口,允许便携式访问到底层数据库,因此可以用 Java 编写不同类型的可执行文件,例如:
- Java 应用程序
- Java Applets
- Java Servlets
- Java ServerPages (JSPs)
- Enterprise JavaBeans (EJBs)
所有这些不同的可执行文件就可以使用 JDBC 驱动程序来访问数据库,这样可以方便的访问数据。
JDBC 具有 ODBC 一样的性能,允许 Java 程序包含与数据库无关的代码。
先决条件
为了更好的理解本教程,需要对以下两个主题内容很好的理解:
- 核心 JAVA 编程
-
JDBC 架构
JDBC 的 API 支持两层和三层处理模式进行数据库访问,但一般的 JDBC 架构由两层处理模式组成:
JDBC API: 提供了应用程序对 JDBC 管理器的连接。
- JDBC Driver API: 提供了 JDBC 管理器对驱动程序连接。
JDBC API 使用驱动程序管理器和数据库特定的驱动程序来提供异构(heterogeneous)数据库的透明连接。
JDBC 驱动程序管理器可确保正确的驱动程序来访问每个数据源。该驱动程序管理器能够支持连接到多个异构数据库的多个并发的驱动程序。
以下是结构图,其中显示了驱动程序管理器相对于在 JDBC 驱动程序和 Java 应用程序所处的位置。
常见的 JDBC 组件
JDBC 的 API 提供了以下接口和类:
DriverManager :这个类管理一系列数据库驱动程序。匹配连接使用通信子协议从 JAVA 应用程序中请求合适的数据库驱动程序。识别 JDBC 下某个子协议的第一驱动程序将被用于建立数据库连接。
Driver : 这个接口处理与数据库服务器的通信。你将很少直接与驱动程序互动。相反,你使用 DriverManager 中的对象,它管理此类型的对象。它也抽象与驱动程序对象工作相关的详细信息。
Connection : 此接口具有接触数据库的所有方法。该连接对象表示通信上下文,即,所有与数据库的通信仅通过这个连接对象进行。
Statement : 使用创建于这个接口的对象将 SQL 语句提交到数据库。除了执行存储过程以外,一些派生的接口也接受参数。
ResultSet : 在你使用语句对象执行 SQL 查询后,这些对象保存从数据获得的数据。它作为一个迭代器,让您可以通过它的数据来移动。
SQLException : 这个类处理发生在数据库应用程序的任何错误。
二、JDBC SQL语法
SQL语法
结构化查询语言(SQL)是一种标准化的语言,它可以让你对数据库进行操作,如创建项目,读取内容,更新内容和删除项目。
SQL 支持你可能会使用到的任何数据库,它可以让你编写独立于底层数据库的数据库代码。
本章介绍了 SQL,这是一个了解 JDBC 概念的先决条件。在经历了这一章后,你将能够创建,读取,更新和删除(通常被称为 CRUD 操作)一个数据库中的数据。
创建 JDBC 应用程序
构建一个 JDBC 应用程序包括以下六个步骤-
- 导入数据包:需要你导入含有需要进行数据库编程的 JDBC 类的包。大多数情况下,使用 import java.sql. 就足够了。
- 注册 JDBC 驱动器:需要你初始化一个驱动器,以便于你打开一个与数据库的通信通道。
- 打开连接:需要使用 DriverManager.getConnection() 方法创建一个 Connection 对象,它代表与数据库的物理连接。
- 执行查询:需要使用类型声明的对象建立并提交一个 SQL 语句到数据库。
- 提取结果数据:要求使用适当的 ResultSet.getXXX() 方法从结果集中检索数据。
- 清理环境:依靠 JVM 的垃圾收集来关闭所有需要明确关闭的数据库资源。
示例代码
当你在未来需要创建自己的 JDBC 应用程序时,本示例可以作为一个模板。
在前面的章节里,基于对环境和数据库安装的示例代码已经写过。
将下面的示例拷贝并粘帖到 JDBCExample.java 中,编译并运行它,如下所示- ```sql //STEP 1. Import required packages import java.sql.*;
public class FirstExample {
// JDBC driver name and database URL
static final String JDBC_DRIVER = “com.mysql.jdbc.Driver”;
static final String DB_URL = “jdbc:mysql://localhost/EMP”;
// Database credentials static final String USER = “username”; static final String PASS = “password”;
public static void main(String[] args) { Connection conn = null; Statement stmt = null; try{ //STEP 2: Register JDBC driver Class.forName(“com.mysql.jdbc.Driver”);
//STEP 3: Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//STEP 4: Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
//STEP 5: Extract data from result set
while(rs.next()){
//Retrieve by column name
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
//Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
}
//STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){ //Handle errors for JDBC se.printStackTrace(); }catch(Exception e){ //Handle errors for Class.forName e.printStackTrace(); }finally{ //finally block used to close resources try{ if(stmt!=null) stmt.close(); }catch(SQLException se2){ }// nothing we can do try{ if(conn!=null) conn.close(); }catch(SQLException se){ se.printStackTrace(); }//end finally try }//end try System.out.println(“Goodbye!”); }//end main }//end FirstExample
当你运行 **JDBCExample** 时,它将展示下面的结果-
```sql
Connecting to database...
Creating statement...
ID: 100, Age: 18, First: Zara, Last: Ali
ID: 101, Age: 25, First: Mahnaz, Last: Fatma
ID: 102, Age: 30, First: Zaid, Last: Khan
ID: 103, Age: 28, First: Sumit, Last: Mittal
三、JDBC 驱动
什么是 JDBC 驱动程序?
JDBC 驱动实现了 JDBC API 中定义的接口,该接口用于与数据库服务器进行交互。
例如,使用 JDBC 驱动程序可以让你打开数据库连接,并通过发送 SQL 或数据库命令,然后通过 Java 接收结果。
java.sql 包中附带的 JDK,包含了定义各种类与他们的行为和实际实现,这些类都在第三方驱动程序中完成。第三方供应商在他们的数据库驱动程序中都实现了 java.sql.Driver 接口。
JDBC 驱动程序类型
JDBC 驱动程序的实现,因为各种各样的操作系统和 Java 运行在硬件平台的不同而不同。Sun 公司将实现类型分为四类:类型1,2,3,4,其解释如下-
类型1:JDBC-ODBC 桥驱动程序:
在类型1驱动程序中,一个 JDBC 桥接器是用来访问安装在每个客户机上的 ODBC 驱动程序。为了使用 ODBC,需要在目标数据库上配置系统数据源名称(DSN)。
当 Java 刚出来时,这是一个很有用的驱动程序,因为大多数的数据库只支持 ODBC 访问,但现在此类型的驱动程序仅适用于实验用途或在没有其他选择的情况。
自带 JDK 1.2 中的 JDBC-ODBC 桥是这类驱动程序的一个很好的例子。
类型2:JDBC-Native API
在类型2驱动程序中,JDBC API 调用转换成原生的 C/C++ API 调用,这对于数据库来说具有唯一性。这些驱动程序通常由数据库供应商提供,并和 JDBC-ODBC 桥驱动程序同样的方式使用。该供应商的驱动程序必须安装在每台客户机上。
如果我们改变了当前数据库,我们必须改变原生 API ,因为它是具体到某一个数据库,并且他们大多已经失效了。即使这样用类型2驱动程序也能提高一些速度,因为他消除了 ODBC 的开销。
Oracle 调用接口(OCI)驱动程序是一个类型2驱动程序的示例。
类型3:JDBC-Net 纯 Java
在类型3驱动程序中,一般用三层方法来访问数据库。JDBC 客户端使用标准的网络套接字与中间件应用服务器进行通信。套接字的相关信息被中间件应用服务器转换为数据库管理系统所要求的的调用格式,并转发到数据库服务器。
这种驱动程序是非常灵活的,因为它不需要在客户端上安装代码,而且单个驱动程序能提供访问多个数据库。
你可以将应用服务器作为一个 JDBC “代理”,这意味着它会调用客户端应用程序。因此,你需要一些有关服务器配置方面的知识,这样可以高效地使用此驱动程序类型。
你的应用服务器可能使用类型1,2,或4驱动程序与数据库进行通信,了解它们的细微之处将会很有帮助。
类型4:100%纯 Java
在类型4驱动程序中,一个纯粹的基于 Java 的驱动程序通过 socket 连接与供应商的数据库进行通信。这是可用于数据库的最高性能的驱动程序,并且通常由供应商自身提供。
这种驱动器是非常灵活的,你不需要在客户端或服务端上安装特殊的软件。此外,这些驱动程序是可以动态下载的。
MySQL Connector/J 的驱动程序是一个类型4驱动程序。因为它们的网络协议的专有属性,数据库供应商通常提供类型4的驱动程序。
该使用哪种驱动程序?
如果你正在访问一个数据库,如 Oracle,Sybase 或 IBM,首选的驱动程序是类型4。
如果你的 Java 应用程序同时访问多个数据库类型,类型3是首选的驱动程序。
类型2驱动程序是在你的数据库没有提供类型3或类型4驱动程序时使用的。
类型1驱动程序不被认为是部署级的驱动程序,它存在的目的通常仅用于开发和测试。
四、JDBC 连接数据库
连接数据库
在你安装相应的驱动程序后,就可以用 JDBC 建立一个数据库连接。
编写建立一个 JDBC 连接的程序是相当简单的。下面是简单的四个步骤-
- 导入 JDBC 包:在你的 Java 代码中,用 import 语句添加你所需的类。
- 注册 JDBC 驱动程序:这一步会导致 JVM 加载所需的驱动程序到内存中执行,因此它可以实现你的 JDBC 请求。
- 数据库 URL 制定:这是用来创建格式正确的地址指向你想要连接的数据库。
- 创建连接对象:最后,代码调用 DriverManager 对象的 getConnection() 方法来建立实际的数据库连接。
导入 JDBC 包
import 语句告诉 Java 编译器在哪里可以找到你在代码中引用的类,这些引用放置在你的源代码起始位置。
使用标准的 JDBC 包,它允许你选择,插入,更新和删除 SQL 表中的数据,添加以下引用到您的源代码中-import java.sql.* ; // for standard JDBC programs
import java.math.* ; // for BigDecimal and BigInteger
注册 JDBC 驱动程序
在使用驱动程序之前,你必须在你的程序里面注册它。通过加载 Oracle 驱动程序的类文件到内存中来注册驱动程序,因此它可以采用 JDBC 接口来实现。
你需要在你的程序里做一次注册即可。你可以通过以下两种方式来注册一个驱动程序。
方法1 - Class.forName()
注册一个驱动程序中最常用的方法是使用 Java 的Class.forName() 方法来动态加载驱动程序的类文件到内存中,它会自动将其注册。这种方法更优越一些,因为它允许你对驱动程序的注册信息进行配置,便于移植。
下面是使用 Class.forName() 来注册 Oracle 驱动程序的示例:
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException ex) {
System.out.println("Error: unable to load driver class!");
System.exit(1);
}
数据库 URL 制定
当你加载了驱动程序之后,你可以通过 DriverManager.getConnection() 方法建立一个连接。为方便参考,以下列出了三个加载 DriverManager.getConnection() 方法:
- getConnection(String url)
- getConnection(String url, Properties prop)
- getConnection(String url, String user, String password)
在这里,每个格式需要一个数据库 URL ,数据库 URL 是指向数据库的地址。
在建立一个数据连接的时候,大多数会在配置一个数据库 URL 时遇到问题。
下表列出了常用的 JDBC 驱动程序名和数据库URL。
RDBMS | JDBC 驱动程序名称 | URL 格式 |
---|---|---|
MySQL | com.mysql.jdbc.Driver | jdbc:mysql://hostname/ databaseName |
ORACLE | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:@hostname:port Number:databaseName |
DB2 | COM.ibm.db2.jdbc.net.DB2Driver | jdbc:db2:hostname:port Number/databaseName |
Sybase | com.sybase.jdbc.SybDriver | jdbc:sybase:Tds:hostname: port Number/databaseName |
URL 格式所有加粗的部分都是静态的,你需要将剩余部分按照你的数据库实际情况进行设置。
创建连接对象
我们已经列出了三种用 DriverManager.getConnection() 方法来创建一个连接对象。
使用数据库 URL 的用户名和密码
getConnection() 最常用的方式是需要你提供一个数据库 URL,用户名和密码:
假设你使用的是 Oracle 的简化驱动程序,你可以从 URL 获得数据库的主机名:端口:数据库名称的信息。
如果你有一台名为 amrood 的主机,它的 TCP / IP 地址 192.0.0.1,你的 Oracle 监听器被配置为监听端口 1521,数据库名称是 EMP,然后完整的数据库 URL 是-
jdbc:oracle:thin:@amrood:1521:EMP
现在,你必须调用适当的用户名和密码以及 getConnection() 方法来获得一个 Connection 对象,如下所示:
String URL = "jdbc:oracle:thin:@amrood:1521:EMP";
String USER = "username";
String PASS = "password"
Connection conn = DriverManager.getConnection(URL, USER, PASS);
只使用数据库 URL
第二种 DriverManager.getConnection() 方法调用的方式只需要数据库 URL 参数-
DriverManager.getConnection(String url);
然而,在这种情况下,数据库的 URL ,包括用户名和密码,将表现为以下的格式-
jdbc:oracle:driver:username/password@database
所以上述连接对象可以如下所示创建连接-
String URL = "jdbc:oracle:thin:username/password@amrood:1521:EMP";
Connection conn = DriverManager.getConnection(URL);
使用数据库 URL 和 Properties 对象
第三种 DriverManager.getConnection() 方法调用需要数据库 URL 和 Properties 对象-
DriverManager.getConnection(String url, Properties info);
Properties 对象保存了一组关键数值。它通过调用 getConnection() 方法,将驱动程序属性传递给驱动程序。
使用下面的代码可以建立与上述示例相同的连接-
import java.util.*;
String URL = "jdbc:oracle:thin:@amrood:1521:EMP";
Properties info = new Properties( );
info.put( "user", "username" );
info.put( "password", "password" );
Connection conn = DriverManager.getConnection(URL, info);
关闭 JDBC 连接
在 JDBC 程序的末尾,它必须明确关闭所有的连接到数据库的连接,以结束每个数据库会话。但是,如果忘了,Java 垃圾收集器也会关闭连接,它会完全清除过期的对象。
依托垃圾收集器,特别是在数据库编程,是非常差的编程习惯。你应该养成用 close()方法关闭连接对象的习惯。
为了确保连接被关闭,你可以在代码中的 ‘finally’ 程序块中执行。 无论异常是否发生,finally 程序是肯定会被执行的。
要关闭上面打开的连接,你应该调用 close()方法,如下所示-
conn.close();
五、JDBC Statement 对象
Statement 对象
一旦我们获得了数据库的连接,我们就可以和数据库进行交互。JDBC 的 Statement,CallableStatement 和 PreparedStatement 接口定义的方法和属性,可以让你发送 SQL 命令或 PL/SQL 命令到数据库,并从你的数据库接收数据。
在数据库中,它们还定义了帮助 Java 和 SQL 数据类型之间转换数据差异的方法。
下表提供了每个接口的用途概要,根据实际目的决定使用哪个接口。
接口 | 推荐使用 |
---|---|
Statement | 可以正常访问数据库,适用于运行静态 SQL 语句。 Statement 接口不接受参数。 |
PreparedStatement | 计划多次使用 SQL 语句, PreparedStatement 接口运行时接受输入的参数。 |
CallableStatement | 适用于当你要访问数据库存储过程的时候, CallableStatement 接口运行时也接受输入的参数。 |
Statement 对象
创建 Statement 对象
在你准备使用 Statement 对象执行 SQL 语句之前,你需要使用 Connection 对象的 createStatement() 方法创建一个,如下面的示例所示
Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
当你创建了一个 Statement 对象之后,你可以用它的三个执行方法的任一方法来执行 SQL 语句。
- boolean execute(String SQL) : 如果 ResultSet 对象可以被检索,则返回的布尔值为 true ,否则返回 false 。当你需要使用真正的动态 SQL 时,可以使用这个方法来执行 SQL DDL 语句。
- int executeUpdate(String SQL) : 返回执行 SQL 语句影响的行的数目。使用该方法来执行 SQL 语句,是希望得到一些受影响的行的数目,例如,INSERT,UPDATE 或 DELETE 语句。
- ResultSet executeQuery(String SQL) : 返回一个 ResultSet 对象。当你希望得到一个结果集时使用该方法,就像你使用一个 SELECT 语句。
关闭 Statement 对象
正如你关闭一个 Connection 对象来节约数据库资源,出于同样的原因你也应该关闭 Statement 对象。
简单的调用 close() 方法就可以完成这项工作。如果你关闭了 Connection 对象,那么它也会关闭 Statement 对象。然而,你应该始终明确关闭 Statement 对象,以确保真正的清除。Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
stmt.close();
}
PreparedStatement 对象
PreparedStatement 接口扩展了 Statement 接口,它让你用一个常用的 Statement 对象增加几个高级功能。
这个 statement 对象可以提供灵活多变的动态参数。创建 PreparedStatement 对象
JDBC 中所有的参数都被用 ? 符号表示,这是已知的参数标记。在执行 SQL 语句之前,你必须赋予每一个参数确切的数值。PreparedStatement pstmt = null;
try {
String SQL = "Update Employees SET age = ? WHERE id = ?";
pstmt = conn.prepareStatement(SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
setXXX() 方法将值绑定到参数,其中 XXX 表示你希望绑定到输入参数的 Java 数据类型。如果你忘了赋予值,你将收到一个 SQLException。
每个参数标记映射它的序号位置。第一标记表示位置 1 ,下一个位置为 2 等等。这种方法不同于 Java 数组索引,它是从 0 开始的。
所有的 Statement对象 的方法都与数据库交互,(a) execute(),(b) executeQuery(),及 (c) executeUpdate() 也能被 PreparedStatement 对象引用。然而,这些方法被 SQL 语句修改后是可以输入参数的。关闭 PreparedStatement 对象
正如你关闭一个 Statement 对象,出于同样的原因,你也应该关闭 PreparedStatement 对象。
简单的调用 close() 方法可以完成这项工作。如果你关闭了 Connection 对象,那么它也会关闭 PreparedStatement 对象。然而,你应该始终明确关闭 PreparedStatement 对象,以确保真正的清除。
```sql import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;PreparedStatement pstmt = null;
try {
String SQL = "Update Employees SET age = ? WHERE id = ?";
pstmt = conn.prepareStatement(SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
pstmt.close();
}
public class JDBCExample { static final String DB_URL = “jdbc:mysql://localhost/TUTORIALSPOINT”; static final String USER = “guest”; static final String PASS = “guest123”; static final String QUERY = “SELECT id, first, last, age FROM Employees”; static final String UPDATE_QUERY = “UPDATE Employees set age=30 WHERE id=103”;
public static void main(String[] args) { // Open a connection try(Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); Statement stmt = conn.createStatement(); ) { // Let us check if it returns a true Result Set or not. Boolean ret = stmt.execute(UPDATE_QUERY); System.out.println(“Return value is : “ + ret.toString() );
// Let us update age of the record with ID = 103;
int rows = stmt.executeUpdate(UPDATE_QUERY);
System.out.println("Rows impacted : " + rows );
// Let us select all the records and display them.
ResultSet rs = stmt.executeQuery(QUERY);
// Extract data from result set
while (rs.next()) {
// Retrieve by column name
System.out.print("ID: " + rs.getInt("id"));
System.out.print(", Age: " + rs.getInt("age"));
System.out.print(", First: " + rs.getString("first"));
System.out.println(", Last: " + rs.getString("last"));
}
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
} }
<a name="yg9KQ"></a>
# 六、JDBC 结果集
<a name="eEhDi"></a>
## 结果集
SQL 语句从数据库查询中获取数据,并将数据返回到结果集中。SELECT 语句是一种标准的方法,它从一个数据库中选择行记录,并显示在一个结果集中。 _java.sql.ResultSet_ 接口表示一个数据库查询的结果集。<br />一个 ResultSet 对象控制一个光标指向当前行的结果集。术语“结果集”是指包含在 ResultSet 对象中的行和列的数据。<br />ResultSet 接口的方法可细分为三类-
- **导航方法:**用于移动光标。
- **获取方法:**用于查看当前行被光标所指向的列中的数据。
- **更新方法:**用于更新当前行的列中的数据。这些更新也会更新数据库中的数据。
光标的移动基于 ResultSet 的属性。用相应的语句生成 ResultSet 对象时,同时生成 ResultSet 的属性。<br />JDBC 提供了连接方法通过下列创建语句来生成你所需的 ResultSet 对象:
- **createStatement(int RSType, int RSConcurrency);**
- **prepareStatement(String SQL, int RSType, int RSConcurrency);**
- **prepareCall(String sql, int RSType, int RSConcurrency);**
第一个参数表示 ResultSet 对象的类型,第二个参数是两个 ResultSet 常量之一,该常量用于判断该结果集是只读的还是可修改的。
<a name="tdfqS"></a>
## ResultSet 的类型
可能的 RSType 如下所示。如果你不指定 ResultSet 类型,将自动获得的值是 TYPE_FORWARD_ONLY。
| 类型 | 描述 |
| --- | --- |
| ResultSet.TYPE_FORWARD_ONLY | 光标只能在结果集中向前移动。 |
| ResultSet.TYPE_SCROLL_INSENSITIVE | 光标可以向前和向后移动。当结果集创建后,其他人对数据库的操作不会影响结果集的数据。 |
| ResultSet.TYPE_SCROLL_SENSITIVE. | 光标可以向前和向后移动。当结果集创建后,其他人对数据库的操作会影响结果集的数据。 |
<a name="c74uU"></a>
## ResultSet 的并发性
RSConcurrency 的值如下所示,如果你不指定并发类型,将自动获得的值是 CONCUR_READ_ONLY。
| 并发性 | 描述 |
| --- | --- |
| ResultSet.CONCUR_READ_ONLY | 创建一个只读结果集,这是默认的值。 |
| ResultSet.CONCUR_UPDATABLE | 创建一个可修改的结果集。 |
到目前为止我们的示例可以如下所示,可以写成初始化一个 Statement 对象来创建一个只能前进,而且只读的 ResultSet 对象-
```sql
try {
Statement stmt = conn.createStatement(
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
}
catch(Exception ex) {
....
}
finally {
....
}
导航结果集
在 ResultSet 接口中包括如下几种方法涉及移动光标-
S.N. | 方法 & 描述 |
---|---|
1 | public void beforeFirst() throws SQLException 将光标移动到第一行之前。 |
2 | public void afterLast() throws SQLException 将光标移动到最后一行之后。 |
3 | public boolean first() throws SQLException 将光标移动到第一行。 |
4 | public void last() throws SQLException 将光标移动到最后一行。 |
5 | public boolean absolute(int row) throws SQLException 将光标移动到指定的第 row 行。 |
6 | public boolean relative(int row) throws SQLException 将光标移动到当前指向的位置往前或往后第 row 行的位置。 |
7 | public boolean previous() throws SQLException 将光标移动到上一行,如果超过结果集的范围则返回 false。 |
8 | public boolean next() throws SQLException 将光标移动到下一行,如果是结果集的最后一行则返回 false。 |
9 | public int getRow() throws SQLException 返回当前光标指向的行数的值。 |
10 | public void moveToInsertRow() throws SQLException 将光标移动到结果集中指定的行,可以在数据库中插入新的一行。当前光标位置将被记住。 |
11 | public void moveToCurrentRow() throws SQLException 如果光标处于插入行,则将光标返回到当前行,其他情况下,这个方法不执行任何操作。 |
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCExample {
static final String DB_URL = "jdbc:mysql://localhost/TUTORIALSPOINT";
static final String USER = "guest";
static final String PASS = "guest123";
static final String QUERY = "SELECT id, first, last, age FROM Employees";
public static void main(String[] args) {
// Open a connection
try(Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery(QUERY);
) {
// Move cursor to the last row.
System.out.println("Moving cursor to the last...");
rs.last();
// Extract data from result set
System.out.println("Displaying record...");
//Retrieve by column name
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
// Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
// Move cursor to the first row.
System.out.println("Moving cursor to the first row...");
rs.first();
// Extract data from result set
System.out.println("Displaying record...");
// Retrieve by column name
id = rs.getInt("id");
age = rs.getInt("age");
first = rs.getString("first");
last = rs.getString("last");
// Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
// Move cursor to the first row.
System.out.println("Moving cursor to the next row...");
rs.next();
// Extract data from result set
System.out.println("Displaying record...");
id = rs.getInt("id");
age = rs.getInt("age");
first = rs.getString("first");
last = rs.getString("last");
// Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
查看结果集
ResultSet接口中含有几十种从当前行获取数据的方法。
每个可能的数据类型都有一个 get 方法,并且每个 get 方法有两个版本-
- 一个需要列名。
- 一个需要列的索引。
例如,如果你想查看的列包含一个 int 类型,你需要在 ResultSet 中调用 getInt()方法-
序列号 | 方法 & 描述 |
---|---|
1 | public int getInt(String columnName) throws SQLException 返回当前行中名为 columnName 的列的 int 值。 |
2 | public int getInt(int columnIndex) throws SQLException 返回当前行中指定列的索引的 int 值。列索引从 1 开始,意味着行中的第一列是 1 ,第二列是 2 ,以此类推。 |
同样的,在 ResultSet 接口中还有获取八个 Java 原始类型的 get 方法,以及常见的类型,比如 java.lang.String,java.lang.Object 和 java.net.URL。
也有用于获取 SQL 数据类型 java.sql.Date, java.sql.Time, java.sql.Timestamp, java.sql.Clob,java.sql.Blob 中的方法。查看文档可以了解使用这些 SQL 数据类型的更多的信息。
更新的结果集
ResultSet 接口包含了一系列的更新方法,该方法用于更新结果集中的数据。
用 get 方法可以有两个更新方法来更新任一数据类型-
- 一个需要列名。
- 一个需要列的索引。
例如,要更新一个结果集的当前行的 String 列,你可以使用任一如下所示的 updateString()方法-
S.N. | 方法 & 描述 |
---|---|
1 | public void updateString(int columnIndex, String s) throws SQLException 将指定列的字符串的值改为 s。 |
2 | public void updateString(String columnName, String s) throws SQLException 类似于前面的方法,不同之处在于指定的列是用名字来指定的,而不是它的索引。 |
八个原始数据类型都有其更新方法,比如 String,Object,URL,和在 java.sql 包中的 SQL 数据类型。
更新结果集中的行将改变当前行的列中的 ResultSet 对象,而不是基础数据库中的数据。要更新数据库中一行的数据,你需要调用以下的任一方法-
S.N. | 方法 & 描述 |
---|---|
1 | public void updateRow() 通过更新数据库中相对应的行来更新当前行。 |
2 | public void deleteRow() 从数据库中删除当前行。 |
3 | public void refreshRow() 在结果集中刷新数据,以反映数据库中最新的数据变化。 |
4 | public void cancelRowUpdates() 取消对当前行的任何修改。 |
5 | public void insertRow() 在数据库中插入一行。本方法只有在光标指向插入行的时候才能被调用。 |
七、JDBC 数据类型
数据类型
JDBC 驱动程序在将 Java 数据类型发送到数据库之前,会将其转换为相应的 JDBC 类型。对于大多数数据类型都采用了默认的映射关系。例如,一个 Java int 数据类型转换为 SQL INTEGER。通过默认的映射关系来提供驱动程序之间的一致性。
当你调用 PreparedStatement 中的 setXXX()方法或 CallableStatement 对象或 ResultSet.updateXXX()方法时, Java 数据类型会转换为默认的 JDBC 数据类型,如下表概述。
SQL | JDBC/Java | setXXX | updateXXX |
---|---|---|---|
VARCHAR | java.lang.String | setString | updateString |
CHAR | java.lang.String | setString | updateString |
LONGVARCHAR | java.lang.String | setString | updateString |
BIT | boolean | setBoolean | updateBoolean |
NUMERIC | java.math.BigDecimal | setBigDecimal | updateBigDecimal |
TINYINT | byte | setByte | updateByte |
SMALLINT | short | setShort | updateShort |
INTEGER | int | setInt | updateInt |
BIGINT | long | setLong | updateLong |
REAL | float | setFloat | updateFloat |
FLOAT | float | setFloat | updateFloat |
DOUBLE | double | setDouble | updateDouble |
VARBINARY | byte[ ] | setBytes | updateBytes |
BINARY | byte[ ] | setBytes | updateBytes |
DATE | java.sql.Date | setDate | updateDate |
TIME | java.sql.Time | setTime | updateTime |
TIMESTAMP | java.sql.Timestamp | setTimestamp | updateTimestamp |
CLOB | java.sql.Clob | setClob | updateClob |
BLOB | java.sql.Blob | setBlob | updateBlob |
ARRAY | java.sql.Array | setARRAY | updateARRAY |
REF | java.sql.Ref | SetRef | updateRef |
STRUCT | java.sql.Struct | SetStruct | updateStruct |
JDBC 3.0 增强了对 BLOB,CLOB,ARRAY 和 REF 数据类型的支持。 ResultSet 对象现在有 UPDATEBLOB(),updateCLOB(), updateArray(),和 updateRef()方法,通过这些方法你可以直接操作服务器上的相应数据。
你能用 setXXX()方法和 updateXXX()方法将 Java 类型转换为特定的 JDBC 数据类型。你能用 setObject()方法和 updateObject()方法将绝大部分的 Java 类型映射到 JDBC 数据类型。
ResultSet 对象为任一数据类型提供相应的 getXXX()方法,该方法可以获取任一数据类型的列值。上述任一方法的使用需要列名或它的顺序位置。
SQL | JDBC/Java | setXXX | getXXX |
---|---|---|---|
VARCHAR | java.lang.String | setString | getString |
CHAR | java.lang.String | setString | getString |
LONGVARCHAR | java.lang.String | setString | getString |
BIT | boolean | setBoolean | getBoolean |
NUMERIC | java.math.BigDecimal | setBigDecimal | getBigDecimal |
TINYINT | byte | setByte | getByte |
SMALLINT | short | setShort | getShort |
INTEGER | int | setInt | getInt |
BIGINT | long | setLong | getLong |
REAL | float | setFloat | getFloat |
FLOAT | float | setFloat | getFloat |
DOUBLE | double | setDouble | getDouble |
VARBINARY | byte[ ] | setBytes | getBytes |
BINARY | byte[ ] | setBytes | getBytes |
DATE | java.sql.Date | setDate | getDate |
TIME | java.sql.Time | setTime | getTime |
TIMESTAMP | java.sql.Timestamp | setTimestamp | getTimestamp |
CLOB | java.sql.Clob | setClob | getClob |
BLOB | java.sql.Blob | setBlob | getBlob |
ARRAY | java.sql.Array | setARRAY | getARRAY |
REF | java.sql.Ref | SetRef | getRef |
STRUCT | java.sql.Struct | SetStruct | getStruct |
日期和时间数据类型
java.sql.Date 类映射 SQL DATE 类型,java.sql.Time 类和 java.sql.Timestamp 类也分别映射 SQL TIME 数据类型和 SQL TIMESTAMP 数据类型。
以下示例显示了日期和时间类如何转换成标准的 Java 日期和时间值,并匹配成 SQL 数据类型所要求的格式。
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.*;
public class SqlDateTime {
public static void main(String[] args) {
//Get standard date and time
java.util.Date javaDate = new java.util.Date();
long javaTime = javaDate.getTime();
System.out.println("The Java Date is:" +
javaDate.toString());
//Get and display SQL DATE
java.sql.Date sqlDate = new java.sql.Date(javaTime);
System.out.println("The SQL DATE is: " +
sqlDate.toString());
//Get and display SQL TIME
java.sql.Time sqlTime = new java.sql.Time(javaTime);
System.out.println("The SQL TIME is: " +
sqlTime.toString());
//Get and display SQL TIMESTAMP
java.sql.Timestamp sqlTimestamp =
new java.sql.Timestamp(javaTime);
System.out.println("The SQL TIMESTAMP is: " +
sqlTimestamp.toString());
}//end main
}//end SqlDateTime
当你运行 JDBCExample 时,它将展示下面的结果-
The Java Date is:Tue Aug 18 13:46:02 GMT+04:00 2009
The SQL DATE is: 2009-08-18
The SQL TIME is: 13:46:02
The SQL TIMESTAMP is: 2009-08-18 13:46:02.828
处理 NULL 值
SQL 使用 NULL 值和 Java 使用 null 是不同的概念。那么,你可以使用三种策略来处理 Java 中的 SQL NULL 值-
- 避免使用返回原始数据类型的 getXXX()方法。
- 使用包装类的基本数据类型,并使用 ResultSet 对象的 wasNull()方法来测试收到 getXXX()方法返回的值是否为 null,如果是 null,该包装类变量则被设置为 null。
- 使用原始数据类型和 ResultSet 对象的 wasNull()方法来测试通过 getXXX()方法返回的值,如果是 null,则原始变量应设置为可接受的值来代表 NULL。
下面是一个处理 NULL 值的示例-
Statement stmt = conn.createStatement( );
String sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
int id = rs.getInt(1);
if( rs.wasNull( ) ) {
id = 0;
}
八、JDBC 异常
异常
异常处理可以允许你处理一个异常情况,例如可控方式的程序定义错误。
当异常情况发生时,将抛出一个异常。抛出这个词意味着当前执行的程序停止,控制器被重定向到最近的适用的 catch 子句。如果没有适用的 catch 子句存在,那么程序执行被终止。
JDBC 的异常处理是非常类似于 Java 的异常处理,但对于 JDBC,最常见的异常是 java.sql.SQLException。
SQLException 方法
SQLException 异常在驱动程序和数据库中都可能出现。当出现这个异常时,SQLException 类型的对象将被传递到 catch 子句。
传递的 SQLException 对象具有以下的方法,以下的方法可用于检索该异常的额外信息-
方法 | 描述 |
---|---|
getErrorCode( ) | 获取与异常关联的错误号。 |
getMessage( ) | 获取 JDBC 驱动程序的错误信息,该错误是由驱动程序处理的,或者在数据库错误中获取 Oracl 错误号和错误信息。 |
getSQLState( ) | 获取 XOPEN SQLstate 字符串。对于 JDBC 驱动程序错误,使用该方法不能返回有用的信息。对于数据库错误,返回第五位的 XOPEN SQLstate 代码。该方法可以返回 null。 |
getNextException( ) | 获取异常链的下一个 Exception 对象。 |
printStackTrace( ) | 打印当前异常或者抛出,其回溯到标准的流错误。 |
printStackTrace(PrintStream s) | 打印该抛出,其回溯到你指定的打印流。 |
printStackTrace(PrintWriter w) | 打印该抛出,其回溯到你指定的打印写入。 |
通过利用可从 Exception 对象提供的信息,你可以捕获异常并继续运行程序。这是一个 try 块的一般格式-
try {
// Your risky code goes between these curly braces!!!
}
catch(Exception ex) {
// Your exception handling code goes between these
// curly braces, similar to the exception clause
// in a PL/SQL block.
}
finally {
// Your must-always-be-executed code goes between these
// curly braces. Like closing database connection.
}
示例
研究学习下面的示例代码来了解 try …. catch … finally 块的使用。
//STEP 1. Import required packages
import java.sql.*;
public class JDBCExample {
// JDBC driver name and database URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EMP";
// Database credentials
static final String USER = "username";
static final String PASS = "password";
public static void main(String[] args) {
Connection conn = null;
try{
//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//STEP 3: Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//STEP 4: Execute a query
System.out.println("Creating statement...");
Statement stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
//STEP 5: Extract data from result set
while(rs.next()){
//Retrieve by column name
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
//Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
}
//STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
}//end JDBCExample
当你运行 JDBCExample 时,如果没有问题它将展示下面的结果,否则相应的错误将被捕获并会显示错误消息-
Connecting to database...
Creating statement...
ID: 100, Age: 18, First: Zara, Last: Ali
ID: 101, Age: 25, First: Mahnaz, Last: Fatma
ID: 102, Age: 30, First: Zaid, Last: Khan
ID: 103, Age: 28, First: Sumit, Last: Mittal