JDBC知识点(初学用)
J D B C-Java Database Connectivity(Java语言连接数据库)
简介
-
JDBC是SUN公司制定的一套接口(interface)
java.sql.*;(这个软件包下有很多接口)
接口都有调用者和实现者。
面向接口调用,面向接口写实现类,这都属于面向接口编程。
-
为什么要面向接口编程
解耦合:降低程序的耦合度,提高程序的扩展力。
多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
建议:
Animal a = new Cat();
Animal a = new Dog();
public void feed (Animal a){ // 面向父类编程
}
不建议:
Dog d = new Dog();
Cat c = new Cat();
-
思考:为什么SUN要定义一套JDBC接口
因为每个数据库的底层实现原理都不一样。
Oracle数据库有自己的原理。
MySQL也有自己的原理
MS SQL server数据库有自己的原理
JDBC的本质:一套接口
1.JDBC SQL语法
结构化查询语言(SQL)是一种标准化语言,允许对数据库执行操作
例如:创建数据记录,读取内容,更新内容和删除数据记录等。
1. 创建数据库
CREATE DATABASE
语句用于创建新的数据库。 语法是 -
SQL> CREATE DATABASE DATABASE_NAME; SQL
示例 以下SQL语句创建一个名为EMP
的数据库 -
SQL> CREATE DATABASE EMP; SQL
2. 删除数据库
DROP DATABASE
语句用于删除存在的数据库。 语法是 -
注意:要创建或删除数据库,需要有数据库服务器的管理员权限。 需要特别小心的是:删除数据库将丢失数据库中存储的所有数据(无法恢复)。
3. 创建表
CREATE TABLE
语句用于创建新表。 语法是 -
SQL> CREATE TABLE table_name ( column_name column_data_type, column_name column_data_type, column_name column_data_type ... ); SQL
示例
以下SQL语句创建一个名为Employees
的表,其中包含四列:
SQL> CREATE TABLE Employees ( id INT NOT NULL, age INT NOT NULL, first VARCHAR(255), last VARCHAR(255), PRIMARY KEY ( id ) ); SQL
4. 删除表
DROP TABLE
语句用于删除存在的表。 语法是 -
SQL> DROP TABLE table_name; SQL
示例
以下SQL语句删除名为Employees
的表 -
SQL> DROP TABLE Employees; SQL
5. INSERT数据
INSERT
的语法如下所示,其中column1
,column2
等表示要显示在相应列中的数据值 -
SQL> INSERT INTO table_name VALUES (column1, column2, ...); SQL
示例
以下SQL INSERT
语句在先前创建的Employees
表中插入一个新行 -
SQL> INSERT INTO Employees VALUES (100, 18, 'Max', 'Su'); SQL
6. 查询数据
SELECT
语句用于从数据库检索数据。 SELECT
的语法是 -
SQL> SELECT column_name, column_name, ... FROM table_name WHERE conditions; SQL
WHERE
子句可以使用比较运算符,例如:=
,!=
,<
,>
,<=
和>=
,以及BETWEEN
和LIKE
运算符。
示例
以下SQL语句从Employees
表中选择:age
,first
和last
列,其中id
列为100
-
SQL> SELECT first, last, age FROM Employees WHERE id = 100; SQL
以下SQL语句从Employees
表中选择:age
, first
和 last
列,其中first
列包含Max
-
SQL> SELECT first, last, age FROM Employees WHERE first LIKE '%Max%'; SQL
7. 更新数据
UPDATE
语句用于更新数据。 UPDATE
的语法是 -
SQL> UPDATE table_name SET column_name = value, column_name = value, ... WHERE conditions; SQL
WHERE
子句可以使用比较运算符,例如:=
,!=
,<
,>
,<=
和>=
,以及BETWEEN
和LIKE
运算符。
示例
以下SQL UPDATE
语句更新id
为100
的雇员的age
列的值为:20
,
SQL> UPDATE Employees SET age=20 WHERE id=100; SQL
8. 删除数据
DELETE
语句用于从表中删除数据。 DELETE
的语法是 -
SQL> DELETE FROM table_name WHERE conditions; SQL
WHERE
子句可以使用比较运算符,例如:=
,!=
,<
,>
,<=
和>=
,以及BETWEEN
和LIKE
运算符。
示例
以下SQL DELETE
语句将删除ID
为100
的员工的记录 -
SQL> DELETE FROM Employees WHERE id=100;
2.JDBC简单示例代码
本文章教程中将演示如何创建一个简单的JDBC应用程序的示例。 这将显示如何打开数据库连接,执行SQL查询并显示结果。
这个示例代码中涉及所有步骤,一些步骤将在本教程的后续章节中进行说明。
创建JDBC应用程序
构建JDBC应用程序涉及以下六个步骤 -
-
导入包:需要包含包含数据库编程所需的JDBC类的包。 大多数情况下,使用
import java.sql.*
就足够了。 -
注册JDBC驱动程序:需要初始化驱动程序,以便可以打开与数据库的通信通道。
-
打开一个连接:需要使用
DriverManager.getConnection()
方法创建一个Connection
对象,它表示与数据库的物理连接。 -
执行查询:需要使用类型为
Statement
的对象来构建和提交SQL语句到数据库。 -
从结果集中提取数据:需要使用相应的
ResultSet.getXXX()
方法从结果集中检索数据。 -
清理环境:需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。
示例代码
此示例代码是基于上一章完成的环境和数据库设置之后编写的。
在FirstExample.java (F:\worksp\jdbc\FirstExample.java)中复制并粘贴以下示例,编译并运行如下 -
//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 = "root"; static final String PASS = "123456"; 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("There are so thing wrong!"); }//end main }//end FirstExample Java
把上面代码存放到 F:\worksp\jdbc\FirstExample.java 文件中,并创建一个目录:F:\worksp\jdbc\libs,下载 mysql-connector-java-5.1.40-bin.jar
放入到F:\worksp\jdbc\libs 目录中。
使用命令行编译Java程序并加载指定目录中的Jar包(mysql-connector-java-5.1.40-bin.jar
):
F:\worksp\jdbc> javac -Djava.ext.dirs=./libs FirstExample.java ##-- 或者 F:\worksp\jdbc> javac -Djava.ext.dirs=F:\worksp\jdbc\libs FirstExample.java Shell
编译上面代码后,得到以下结果 -
## F:\worksp\jdbc>javac -Djava.ext.dirs=./libs FirstExample.java ## 运行程序 - F:\worksp\jdbc>java -Djava.ext.dirs=./libs FirstExample Connecting to database... Tue May 30 22:43:18 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Creating statement... ID: 100, Age: 28, First: Max, Last: Su ID: 101, Age: 25, First: Wei, Last: Wang ID: 102, Age: 30, First: Xueyou, Last: Zhang ID: 103, Age: 28, First: Jack, Last: Ma There are so thing wrong! F:\worksp\jdbc> Shell
完整的执行过程如下 -
或者使用 Eclipse
或其它IDE创建代码执行。
3.JDBC驱动程序类型
JDBC驱动程序是什么?
JDBC驱动程序在JDBC API中实现定义的接口,用于与数据库服务器进行交互。
例如,使用JDBC驱动程序,可以通过发送SQL或数据库命令,然后使用Java接收结果来打开数据库连接并与数据库进行交互。
JDK附带的Java.sql
包包含各种类,其类的行为被定义,实现在第三方驱动程序中完成。 第三方供应商在其数据库驱动程序中实现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本地API
在类型2驱动程序中,JDBC API调用将转换为本地C/C++ API调用,这是数据库唯一的。 这些驱动程序通常由数据库供应商提供,并以与JDBC-ODBC桥接相同的方式使用。 必须在每个客户机上安装供应商特定的驱动程序。
如果要更改数据库,则必须更改原生API,因为它特定于数据库,并且现在大部分已经过时,但是使用类型2驱动程序实现了一些扩展功能的开发,它消除了ODBC的开销。
Oracle调用接口(OCI)驱动程序是类型2驱动程序的示例。
类型3:JDBC-Net纯Java
在类型3驱动程序中,使用三层方法访问数据库。 JDBC客户端使用标准网络套接字与中间件应用程序服务器进行通信。 套接字信息随后由中间件应用服务器转换成DBMS所需的调用格式,并转发到数据库服务器。
这种驱动程序是非常灵活的,因为它不需要在客户端上安装代码,一个驱动程序实际上可以提供多个数据库的访问。
可以将应用程序服务器视为JDBC“代理”,它会调用客户端应用程序。 因此,我们需要了解应用程序服务器的配置,才能有效地使用此驱动程序类型。
应用程序服务器可能会使用类型1,2或4驱动程序与数据库通信,了解细微差别对理解JDBC是有帮助的。
类型4:100%纯Java
在类型4驱动程序中,基于纯Java的驱动程序通过套接字连接与供应商的数据库直接通信。 这是数据库可用的最高性能驱动程序,通常由供应商自己提供。
这种驱动是非常灵活的,不需要在客户端或服务器上安装特殊的软件。 此外,这些驱动程序可以动态下载。
MySQL Connector/J
驱动程序是类型4驱动程序。 由于其网络协议的专有性质,数据库供应商通常提供类型4驱动程序。
应该使用哪个驱动程序?
如果您正在访问一种类型的数据库,例如Oracle,Sybase或IBM DB2,则首选驱动程序类型为类型4。
如果Java应用程序同时访问多种类型的数据库,则类型3是首选驱动程序。
类型2驱动程序在数据库不可用的类型3或类型4驱动程序的情况下使用。
类型1驱动程序不被视为部署级驱动程序,通常仅用于开发和测试目的。
4.JDBC数据库连接
安装相应的驱动程序后,现在是时候来学习使用JDBC建立数据库连接了。
建立JDBC连接所涉及的编程相当简单。 以下是基本的四个步骤 -
-
导入JDBC包:使用Java语言的
import
语句在Java代码开头位置导入所需的类。 -
注册JDBC驱动程序:使JVM将所需的驱动程序实现加载到内存中,从而可以满足JDBC请求。
-
数据库URL配置:创建一个正确格式化的地址,指向要连接到的数据库(如:MySQL,Oracle和MSSQL等等)。
-
创建连接对象:最后,调用
DriverManager
对象的getConnection()
方法来建立实际的数据库连接。
1. 导入JDBC包
import
语句告诉Java编译器在哪里找到在代码中引用的类,import
语句一般放置在源代码的开头。 要使用标准的JDBC包,它允许在数据库表中执行选择,插入,更新和删除数据,请将以下导入添加到源代码中 -
import java.sql.* ; // for standard JDBC programs import java.math.* ; // for BigDecimal and BigInteger support Java
2. 注册JDBC驱动程序
在使用程序之前,必须先注册该驱动程序。 注册驱动程序是将Oracle驱动程序的类文件加载到内存中的过程,因此可以将其用作JDBC接口的实现。
只需在程序中一次注册就可以。可以通过两种方式之一来注册驱动程序。
2.1 方法I - 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); } Java
使用JDBC驱动程序连接MySQL数据库的示例代码片段 -
Class.forName("com.mysql.jdbc.Driver"); Connection conn = null; conn = DriverManager.getConnection("jdbc:mysql://hostname:port/db_name","db_username", "db_password"); conn.close(); Java
使用getInstance()
方法来解决不合规的JVM,但是必须编写两个额外的异常,如下所示:
try { Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); } catch(ClassNotFoundException ex) { System.out.println("Error: unable to load driver class!"); System.exit(1); catch(IllegalAccessException ex) { System.out.println("Error: access problem while loading!"); System.exit(2); catch(InstantiationException ex) { System.out.println("Error: unable to instantiate driver!"); System.exit(3); } Java
2.2 方法II - DriverManager.registerDriver()
第二种方法是使用静态DriverManager.registerDriver()
方法来注册驱动程序。
如果使用的是非JDK兼容的JVM(如Microsoft提供的),则应使用registerDriver()
方法。
以下示例使用registerDriver()
注册Oracle驱动程序 -
try { Driver myDriver = new oracle.jdbc.driver.OracleDriver(); DriverManager.registerDriver( myDriver ); } catch(ClassNotFoundException ex) { System.out.println("Error: unable to load driver class!"); System.exit(1); } Java
5.JDBC 数据库交互
当获得了与数据库的连接后,就可以与数据库进行交互了。 JDBC Statement
,CallableStatement
和PreparedStatement
接口定义了可用于发送SQL或PL/SQL命令,并从数据库接收数据的方法和属性。
它们还定义了有助于在Java和SQL数据类型的数据类型差异转换的方法。 下表提供了每个接口定义,以及使用这些接口的目的的总结。
接口 | 推荐使用 |
---|---|
Statement | 用于对数据库进行通用访问,在运行时使用静态SQL语句时很有用。 Statement 接口不能接受参数。 |
PreparedStatement | 当计划要多次使用SQL语句时使用。PreparedStatement 接口在运行时接受输入参数。 |
CallableStatement | 当想要访问数据库存储过程时使用。CallableStatement 接口也可以接受运行时输入参数。 |
1. Statement对象
1.1. 创建Statement对象
在使用Statement
对象执行SQL语句之前,需要使用Connection
对象的createStatement()
方法创建一个Statement
对象,如以下示例所示:
Statement stmt = null; try { stmt = conn.createStatement( ); . . . } catch (SQLException e) { . . . } finally { . . . } Java
在创建Statement
对象后,可以使用它来执行一个SQL语句,它有三个执行方法可以执行。它们分别是 -
-
boolean execute (String SQL)
: 如果可以检索到ResultSet
对象,则返回一个布尔值true
; 否则返回false
。使用此方法执行SQLDDL
语句或需要使用真正的动态SQL,可使用于执行创建数据库,创建表的SQL语句等等。 -
int executeUpdate (String SQL):
返回受SQL语句执行影响的行数。使用此方法执行预期会影响多行的SQL语句,例如:INSERT
,UPDATE
或DELETE
语句。 -
ResultSet executeQuery(String SQL):
返回一个ResultSet
对象。 当您希望获得结果集时,请使用此方法,就像使用SELECT
语句一样。
1.2. 关闭Statement对象
就像关闭一个Connection
对象一样,以保存数据库资源一样,由于同样的原因,还应该关闭Statement
对象。
一个简单的调用close()
方法将执行该作业(工作)。 如果先关闭Connection
对象,它也会关闭Statement
对象。 但是,应该始终显式关闭Statement
对象,以确保正确的清理顺序。
Statement stmt = null; try { stmt = conn.createStatement( ); . . . } catch (SQLException e) { . . . } finally { stmt.close(); } Java
为了更好的理解,建议学习Statment示例教程 。
2. PreparedStatement对象
PreparedStatement
接口扩展了Statement
接口,它添加了比Statement
对象更好一些优点的功能。
此语句可以动态地提供/接受参数。
2.1 创建PreparedStatement对象
PreparedStatement pstmt = null; try { String SQL = "Update Employees SET age = ? WHERE id = ?"; pstmt = conn.prepareStatement(SQL); . . . } catch (SQLException e) { . . . } finally { . . . } Java
JDBC中的所有参数都由 ?
符号作为占位符,这被称为参数标记。 在执行SQL语句之前,必须为每个参数(占位符)提供值。
setXXX()
方法将值绑定到参数,其中XXX
表示要绑定到输入参数的值的Java数据类型。 如果忘记提供绑定值,则将会抛出一个SQLException
。
每个参数标记是它其顺序位置引用。第一个标记表示位置1
,下一个位置2
等等。 该方法与Java数组索引不同(它不从0
开始)。
所有Statement
对象与数据库交互的方法(a)execute()
,(b)executeQuery()
和(c)executeUpdate()
也可以用于PreparedStatement
对象。 但是,这些方法被修改为可以使用输入参数的SQL语句。
2.2. 关闭PreparedStatement对象
就像关闭Statement
对象一样,由于同样的原因(节省数据库系统资源),也应该关闭PreparedStatement
对象。
简单的调用close()
方法将执行关闭。 如果先关闭Connection
对象,它也会关闭PreparedStatement
对象。 但是,应该始终显式关闭PreparedStatement
对象,以确保以正确顺序清理资源。
PreparedStatement pstmt = null; try { String SQL = "Update Employees SET age = ? WHERE id = ?"; pstmt = conn.prepareStatement(SQL); . . . } catch (SQLException e) { . . . } finally { pstmt.close(); } Java
为了更好的理解,建议学习PreparedStatement示例代码 。
3. CallableStatement对象
类似Connection
对象创建Statement
和PreparedStatement
对象一样,它还可以使用同样的方式创建CallableStatement
对象,该对象将用于执行对数据库存储过程的调用。
3.1. 创建CallableStatement对象
假设需要执行以下Oracle存储过程 -
CREATE OR REPLACE PROCEDURE getEmpName (EMP_ID IN NUMBER, EMP_FIRST OUT VARCHAR) AS BEGIN SELECT first INTO EMP_FIRST FROM Employees WHERE ID = EMP_ID; END; SQL
注意:上面的存储过程是针对Oracle编写的,但是如果您使用MySQL数据库,可使用以下方式来编写MySQL相同的存储过程,如下在EMP数据库中创建它 -
DELIMITER $$ DROP PROCEDURE IF EXISTS `EMP`.`getEmpName` $$ CREATE PROCEDURE `EMP`.`getEmpName` (IN EMP_ID INT, OUT EMP_FIRST VARCHAR(255)) BEGIN SELECT first INTO EMP_FIRST FROM Employees WHERE ID = EMP_ID; END $$ DELIMITER ; SQL
存在三种类型的参数:IN
,OUT
和INOUT
。 PreparedStatement
对象只使用IN
参数。CallableStatement
对象可以使用上面三个参数类型。
以下是上面三种类型参数的定义 -
参数 | 描述 |
---|---|
IN | 创建SQL语句时其参数值是未知的。 使用setXXX() 方法将值绑定到IN 参数。 |
OUT | 由SQL语句返回的参数值。可以使用getXXX() 方法从OUT参数中检索值。 |
INOUT | 提供输入和输出值的参数。使用setXXX() 方法绑定变量并使用getXXX() 方法检索值。 |
以下代码片段显示了如何使用Connection.prepareCall()
方法根据上述存储过程来实例化一个CallableStatement
对象 -
CallableStatement cstmt = null; try { String strSQL = "{call getEmpName (?, ?)}"; cstmt = conn.prepareCall (SQL); . . . } catch (SQLException e) { . . . } finally { . . . } Java
String变量strSQL
表示存储过程,带有两个参数占位符。
使用CallableStatement
对象就像使用PreparedStatement
对象一样。 在执行语句之前,必须将值绑定到所有参数,否则将抛出一个SQLException
异常。
如果有IN
参数,只需遵循适用于PreparedStatement
对象的相同规则和技术; 使用与绑定的Java数据类型相对应的setXXX()
方法。
使用OUT
和INOUT
参数时,必须使用一个额外的CallableStatement
对象方法registerOutParameter()
。 registerOutParameter()
方法将JDBC数据类型绑定到存储过程并返回预期数据类型。
当调用存储过程,可以使用适当的getXXX()
方法从OUT
参数中检索该值。 此方法将检索到的SQL类型的值转换为对应的Java数据类型。
关闭CallableStatement对象
就像关闭其他Statement
对象一样,由于同样的原因(节省数据库系统资源),还应该关闭CallableStatement
对象。
简单的调用close()
方法将执行关闭CallableStatemen
t对象。 如果先关闭Connection
对象,它也会关闭CallableStatement
对象。 但是,应该始终显式关闭CallableStatement
对象,以确保按正确顺序的清理资源。
CallableStatement cstmt = null; try { String SQL = "{call getEmpName (?, ?)}"; cstmt = conn.prepareCall (SQL); . . . } catch (SQLException e) { . . . } finally { cstmt.close(); } Java
6.JDBC结果集
SQL语句执行后从数据库查询读取数据,返回的数据放在结果集中。 SELECT
语句用于从数据库中选择行并在结果集中查看它们的标准方法。 java.sql.ResultSet
接口表示数据库查询的结果集。
ResultSet
对象维护指向结果集中当前行的游标。 术语“结果集”是指包含在ResultSet
对象中的行和列数据。
ResultSet
接口的方法可以分为三类:
-
浏览方法:用于移动光标。
-
获取方法:用于查看光标指向的当前行的列中的数据。
-
更新方法:用于更新当前行的列中的数据。 然后在基础数据库中更新数据。
光标可以基于ResultSet
的属性移动。当创建生成ResultSet
的相应Statement
时,将指定这些属性。
JDBC提供以下连接方法来创建具有所需ResultSet
的语句 -
-
createStatement(int RSType, int RSConcurrency);
-
prepareStatement(String SQL, int RSType, int RSConcurrency);
-
prepareCall(String sql, int RSType, int RSConcurrency);
第一个参数表示ResultSet
对象的类型,第二个参数是两个ResultSet
常量之一,用于指定结果集是只读还是可更新。
ResultSet类型
可能的RSType
值如下。如果不指定任何ResultSet
类型,将自动分配一个TYPE_FORWARD_ONLY
值。
类型 | 描述 |
---|---|
ResultSet.TYPE_FORWARD_ONLY | 光标只能在结果集中向前移动。 |
ResultSet.TYPE_SCROLL_INSENSITIVE | 光标可以向前和向后滚动,结果集对创建结果集后发生的数据库所做的更改不敏感。 |
ResultSet.TYPE_SCROLL_SENSITIVE | 光标可以向前和向后滚动,结果集对创建结果集之后发生的其他数据库的更改敏感。 |
ResultSet的并发性
可能的RSConcurrency
如下。 如果不指定任何并发类型,将自动获得一个CONCUR_READ_ONLY
值。
并发 | 描述 |
---|---|
ResultSet.CONCUR_READ_ONLY | 创建只读结果集,这是默认值。 |
ResultSet.CONCUR_UPDATABLE | 创建可更新的结果集 |
到目前为止我们写的所有例子可以写成如下,它初始化一个Statement
对象来创建一个只向前的只读ResultSet
对象 -
try { Statement stmt = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); } catch(Exception ex) { .... } finally { .... } Java
浏览结果集
ResultSet
接口中有几种涉及移动光标的方法,包括 -
编号 | 方法 | 描述 |
---|---|---|
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 | 将光标移动到指定的行。 |
6 | public boolean relative(int row) throws SQLException | 从当前指向的位置,将光标向前或向后移动给定行数。 |
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 | 如果光标当前位于插入行,则将光标移回当前行; 否则,此方法什么也不做 |
查看结果集
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 ,依此类推。 |
类似地,在八个Java基元类型中的每一个的ResultSet
接口中都有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
。
更新结果集
ResultSet
接口包含用于更新结果集的数据的更新方法的集合。
与get方法一样,每种数据类型都有两种更新方法 -
-
一个是采用列名称。
-
另一个采用列索引。
例如,要更新结果集当前行的String
列,可以使用以下updateString()
方法之一:
序号 | 方法 | 描述 |
---|---|---|
1 | public void updateString(int columnIndex, String s) throws SQLException | 将指定列中的String 值更改为指定的s 值。 |
2 | public void updateString(String columnName, String s) throws SQLException | 与前前的方法类似,除了使用列的名称而不是列的索引指定。 |
有八种基本数据类型的更新方法,以及java.sql
包中的String
,Object
,URL
和SQL数据类型。
更新结果集中的一行会更改ResultSet
对象中当前行的列,但不会更改底层数据库中的列的值。 要更新数据库中的行,需要调用以下方法之一。
序号 | 方法 | 描述 |
---|---|---|
1 | public void updateRow() | 更新数据库中当前行 |
2 | public void deleteRow() | 从数据库中删除当前行 |
3 | public void refreshRow() | 刷新结果集中的数据以反映数据库中最近的任何更改。 |
4 | public void cancelRowUpdates() | 取消对当前行所做的任何更新。 |
5 | public void insertRow() | 在数据库中插入一行。 只有当光标指向插入行时,才能调用此方法。 |
7.JDBC数据类型
JDBC驱动程序将Java数据类型转换为适当的JDBC类型,然后将其发送到数据库。 它为大多数数据类型提供并使用默认映射。 例如,Java int
类型会被转换为SQL INTEGER
。 创建默认映射以提供到驱动程序时保持一致性。
下表总结了当调用PreparedStatement
或CallableStatement
对象或ResultSet.updateXXX()
方法的setXXX()
方法时,将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 | updateXXX |
---|---|---|---|
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
数据类型。
以下示例显示了Date
和Time
类如何格式化为标准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 Java
编译并执行上面代码,得到以下结果 -
F:\worksp\jdbc>javac SqlDateTime.java F:\worksp\jdbc>java SqlDateTime The Java Date is:Wed May 31 23:54:57 CST 2017 The SQL DATE is: 2017-05-31 The SQL TIME is: 23:54:57 The SQL TIMESTAMP is: 2017-05-31 23:54:57.937 F:\worksp\jdbc> Shell
处理NULL值
SQL使用NULL
值和Java使用null
是不同的概念。 所以,要在Java中处理SQL NULL
值,可以使用三种策略 -
-
避免使用返回原始数据类型的
getXXX()
方法。 -
对原始数据类型使用包装类,并使用
ResultSet
对象的wasNull()
方法来测试接收getXXX()
方法的返回值的包装器类变量是否应设置为null
。 -
使用原始数据类型和
ResultSet
对象的wasNull()
方法来测试接收到由getXXX()
方法返回的值的原始变量是否应设置为表示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; }
8.JDBC事务
如果JDBC连接处于自动提交模式,默认情况下,则每个SQL语句在完成后都会提交到数据库。
对于简单的应用程序可能没有问题,但是有三个原因需要考虑是否关闭自动提交并管理自己的事务 -
-
提高性能
-
保持业务流程的完整性
-
使用分布式事务
事务能够控制何时更改提交并应用于数据库。 它将单个SQL语句或一组SQL语句视为一个逻辑单元,如果任何语句失败,整个事务将失败。
要启用手动事务支持,而不是使用JDBC驱动程序默认使用的自动提交模式,请调用Connection
对象的setAutoCommit()
方法。 如果将布尔的false
传递给setAutoCommit()
,则关闭自动提交。 也可以传递一个布尔值true
来重新打开它。
例如,如果有一个名为conn
的Connection
对象,请将以下代码关闭自动提交 -
conn.setAutoCommit(false); Java
提交和回滚
完成更改后,若要提交更改,那么可在连接对象上调用commit()
方法,如下所示:
conn.commit( ); Java
否则,要使用连接名为conn
的数据库回滚更新,请使用以下代码 -
conn.rollback( ); Java
以下示例说明了如何使用提交和回滚对象 -
try{ //Assume a valid connection object conn conn.setAutoCommit(false); Statement stmt = conn.createStatement(); String SQL = "INSERT INTO Employees " + "VALUES (106, 20, 'Rita', 'Tez')"; stmt.executeUpdate(SQL); //Submit a malformed SQL statement that breaks String SQL = "INSERTED IN Employees " + "VALUES (107, 22, 'Sita', 'Singh')"; stmt.executeUpdate(SQL); // If there is no error. conn.commit(); }catch(SQLException se){ // If there is any error. conn.rollback(); } Java
在这种情况下,上述INSERT
语句不会成功执行,因为所有操作都被回滚了。
使用保存点
新的JDBC 3.0新添加了Savepoint
接口提供了额外的事务控制能力。大多数现代DBMS支持其环境中的保存点,如Oracle的PL/SQL。
设置保存点(Savepoint
)时,可以在事务中定义逻辑回滚点。 如果通过保存点(Savepoint
)发生错误时,则可以使用回滚方法来撤消所有更改或仅保存保存点之后所做的更改。
Connection
对象有两种新的方法可用来管理保存点 -
-
setSavepoint(String savepointName): - 定义新的保存点,它还返回一个
Savepoint
对象。 -
releaseSavepoint(Savepoint savepointName): - 删除保存点。要注意,它需要一个
Savepoint
对象作为参数。 该对象通常是由setSavepoint()
方法生成的保存点。
有一个rollback (String savepointName)方法,它将使用事务回滚到指定的保存点。
以下示例说明了使用Savepoint
对象 -
try{ //Assume a valid connection object conn conn.setAutoCommit(false); Statement stmt = conn.createStatement(); //set a Savepoint Savepoint savepoint1 = conn.setSavepoint("Savepoint1"); String SQL = "INSERT INTO Employees " + "VALUES (106, 24, 'Curry', 'Stephen')"; stmt.executeUpdate(SQL); //Submit a malformed SQL statement that breaks String SQL = "INSERTED IN Employees " + "VALUES (107, 32, 'Kobe', 'Bryant')"; stmt.executeUpdate(SQL); // If there is no error, commit the changes. conn.commit(); }catch(SQLException se){ // If there is any error. conn.rollback(savepoint1); } Java
在这种情况下,上述INSERT
语句都不会成功,因为所有操作都被回滚了。