JDBC API的那些事,你真的知道吗?

什么是JDBC API?

概念纠正:html

JDBC( Java Data base Connectivity)

如下概念来自官方:
>java

The JDBC API is a Java API for accessing virtually any kind of tabular data. (As a point of interest, JDBC is the trademarked name and is not an acronym; nevertheless, JDBC is often thought of as standing for “Java Database Connectivity.”) The JDBC API consists of a set of classes and interfaces written in the Java programming language that provide a standard API for tool/database developers and makes it possible to write industrial strength database applications using an all-Java APImysql

JDBC API是一种Java API,用于访问几乎任何类型的表格数据。(做为一个兴趣点,JDBC是商标名称,并非首字母缩略词;然而,JDBC一般被认为表明“Java数据库链接”。)JDBC API由一组用Java编写的类和接口组成。为工具/数据库开发人员提供标准API的编程语言,可使用全Java API编写工业级数据库应用程序。程序员

The JDBC API makes it easy to send SQL statements to relational database systems and supports all dialects of SQL. But the JDBC 2.0 API goes beyond SQL, also making it possible to interact with other kinds of data sources, such as files containing tabular data.sql

JDBC API能够轻松地将SQL语句发送到关系数据库系统,并支持全部SQL方言。可是JDBC 2.0 API超越了SQL,也使得与其余类型的数据源(例如包含表格数据的文件)进行交互成为可能。数据库

The value of the JDBC API is that an application can access virtually any data source and run on any platform with a Java Virtual Machine. In other words, with the JDBC API, it isn’t necessary to write one program to access a Sybase database, another program to access an Oracle database, another program to access an IBM DB2 database, and so on. One can write a single program using the JDBC API, and the program will be able to send SQL or other statements to the appropriate data source. And, with an application written in the Java programming language, one doesn’t have to worry about writing different applications to run on different platforms. The combination of the Java platform and the JDBC API lets a programmer write once and run anywhere.编程

JDBC API的价值在于,应用程序几乎能够访问任何数据源,并能够在任何具备Java虚拟机的平台上运行。换句话说,使用JDBC API,没必要编写一个程序来访问Sybase数据库,另外一个程序访问Oracle数据库,另外一个程序访问IBM DB2数据库,等等。可使用JDBC API编写单个程序,程序将可以将SQL或其余语句发送到适当的数据源。并且,使用Java编程语言编写的应用程序,人们没必要担忧编写不一样的应用程序以在不一样的平台上运行。Java平台和JDBC API的结合使程序员能够编写一次并在任何地方运行。数组

The JDBC API is the industry standard for database-independent connectivity between the Java programming language and a wide range of databases. The JDBC API provides a call-level API for SQL-based database access. JDBC technology allows you to use the Java programming language to exploit “Write Once, Run Anywhere” capabilities for applications that require access to enterprise data.安全

JDBC API是Java编程语言与各类数据库之间数据库无关链接的行业标准。JDBC API为基于SQL的数据库访问提供了调用级API。JDBC技术容许您使用Java编程语言为须要访问企业数据的应用程序利用“一次编写,随处运行”功能bash

JDBC API有什么做用?

简单来讲,基于JDBC技术的驱动程序(“JDBC驱动程序”)能够作三件事:

  1. 创建与数据源的链接
  2. 将查询和更新语句发送到数据源
  3. 处理结果

JDBC API主要位于JDK中的java.sql包中(以后扩展的内容位于javax.sql包中),主要包括(斜体表明接口,需驱动程序提供者来具体实现):

  • DriverManager:负责加载各类不一样驱动程序(Driver),并根据不一样的请求,向调用者返回相应的数据库链接(Connection)。
  • Driver
    :驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库链接(Connection)。
  • Connection
    :数据库链接,负责进行与数据库间的通信,SQL执行以及事务处理都是在某个特定Connection环境中进行的。能够产生用以执行SQL的Statement。
  • Statement
    :用以执行SQL查询和更新(针对静态SQL语句和单次执行)。
  • PreparedStatement
    :用以执行包含动态参数的SQL查询和更新(在服务器端编译,容许重复执行以提升效率)。
  • CallableStatement
    :用以调用数据库中的存储过程。
  • SQLException:表明在数据库链接的建立和关闭和SQL语句的执行过程当中发生了例外状况(即错误)。

从根本上来讲,JDBC是一种规范,它提供了一套完整的接口,容许对底层数据库进行可移植的访问,Java能够用于编写不一样类型的可执行文件,例如:

  • Java Applications
  • Java Applets
  • Java Servlets
  • Java ServerPages(JSps)
  • Enterp JavaBeans(EJBs)

这些不一样的可执行文件都可以使用JDBC驱动程序访问数据库,JDBC提供与ODBC相同的功能,容许Java程序包含与数据库无关的代码

两层和三层模型

JDBC API支持用于数据库访问的两层和三层模型。

  1. 数据库访问的双层体系结构
image
image

在双层模型中,Java applet或应用程序直接与数据源对话。这须要一个JDBC驱动程序,它能够与正在访问的特定数据源进行通讯。用户的命令被传递到数据库或其余数据源,而且这些语句的结果被发送回用户。数据源能够位于用户经过网络链接的另外一台机器上。这被称为客户端/服务器配置,用户的机器做为客户端,机器将数据源做为服务器。网络能够是内联网,例如,链接公司内的员工,或者能够是因特网

  1. 数据库访问的三层体系结构
    image
    image

在三层模型中,命令被发送到服务的“中间层”,而后将命令发送到数据源。数据源处理命令并将结果发送回中间层,而后中间层将它们发送给用户。MIS主管发现三层模型很是具备吸引力,由于中间层能够保持对访问的控制以及能够对公司数据进行的更新。另外一个优势是它简化了应用程序的部署。最后,在许多状况下,三层架构能够提供性能优点。直到最近,中间层一般使用C或C ++等语言编写,这些语言提供了快速的性能。可是,随着优化编译器的引入,将Java字节码转换为高效的机器特定代码和技术,例如Enterprise JavaBeanstm,Java平台正迅速成为中间层开发的标准平台。这是一个很大的优点,能够利用Java的健壮性,多线程和安全功能。随着企业愈来愈多地使用Java编程语言编写服务器代码,JDBC API在三层体系结构的中间层中愈来愈多地被使用。使JDBC成为服务器技术的一些功能是它支持链接池,分布式事务和断开链接的行集。固然,JDBC API容许从Java中间层访问数据源。

SQL 一致性

SQL是访问关系数据库的标准语言。不幸的是,SQL尚未像人们想的那样标准化。

一个难点是不一样DBMS(数据库管理系统)使用的数据类型有时会有所不一样,而且变化可能很大。JDBC经过在类中定义一组通用SQL类型标识符来处理此问题java.sql.Types。请注意,在本资料中使用的术语“JDBC SQL类型”,“JDBC类型”和“SQL类型”是可互换的,而且是指在中定义的通用SQL类型标识符java.sql.Types。在“映射SQL和Java类型”一章中有关于数据类型一致性的更完整的讨论。

SQL一致性的另外一个难点是尽管大多数DBMS使用标准形式的SQL来实现基本功能,但它们不符合最近定义的标准SQL语法或更高级功能的语义。例如,并不是全部数据库都支持存储过程或外部联接,而且那些数据库并不老是相互一致。此外,对SQL3功能和数据类型的支持也有很大差别。但愿真正标准的SQL部分将扩展为包含愈来愈多的功能。可是,在此期间,JDBC API必须支持SQL。

JDBC API处理此问题的一种方法是容许将任何查询字符串传递给底层DBMS驱动程序。这意味着应用程序能够根据须要自由使用尽量多的SQL功能,可是它存在在某些DBMS上收到错误的风险。实际上,应用程序查询可能不是SQL,或者它多是为特定DBMS设计的SQL的专用衍生物(例如,用于文档或图像查询)。

JDBC处理SQL一致性问题的第二种方法是提供ODBC样式的转义子句。转义语法为SQL分歧的几个更常见的领域提供了标准的JDBC语法。例如,日期文字和存储过程调用都有转义。

对于复杂的应用程序,JDBC以第三种方式处理SQL一致性。它经过接口提供有关DBMS的描述性信息,DatabaseMetaData以便应用程序能够适应每一个DBMS的要求和功能。可是,典型的最终用户无需担忧元数据。

因为JDBC API用做开发数据库访问工具和其余API的基本API,所以它还必须解决构建在其上的任何内容的一致性问题。JDBC驱动程序必须至少支持ANSI SQL-92 Entry Level。(ANSI SQL-92指的是1992年美国国家标准协会采用的标准。入门级指的是SQL功能的特定列表。)可是,请注意,尽管JDBC 2.0 API包括对SQL3和SQLJ的支持,但JDBC驱动程序不须要支持他们。

鉴于数据库供应商,链接供应商,Internet服务供应商和应用程序编写者普遍接受JDBC API,它已成为Java编程语言数据访问的标准。

常见的JDBC组件

JDBC API提供一下接口和类

  • DriverManager: 该类管理数据库驱动列表.使用通讯自协议匹配来自Java应用程序的链接请求和正确的数据库驱动程序.识别JDBC下某个子协议的第一个驱动程序将用于创建数据库链接.
  • Driver: 此接口处理与数据库服务器的通讯. 您将不多直接与Driver对象进行交互.而是使用DriverManager对象来管理此类对象.它还抽象了与使用Driver对象相关的细节.
  • Connection:此接口包含用于联系数据库的全部方法.链接对象表示通讯上下文.即,与数据库的全部通讯仅经过链接的对象.
  • Statement: 您使用此接口建立的对象将SQL语句提交到数据库,除执行存储过程外,某些派生接口还接受参数.
  • ResultSet: 在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据,它充当迭代器,容许您遍历其数据.
  • SQLException: 此类处理数据库应用程序中发生的任何错误.

JDBC驱动程序类型

JDBC驱动程序实现了在JDBC API中定义的接口,用于于数据库服务器交互。例如:使用JDBC驱动程序能够经过发送SQL或数据库命令,而后使用Java接收结果,让你打开数据库链接并与数据库进行交互。JDK附带的 Java.sql包中包含了各类类,其行为已定义,其实际实如今第三方驱动程序中完成。第三方供应商在其数据库驱动程序中实现java.sql.Driver接口。下图显示了各类驱动程序实现的可能性:

image
image

在开发JDBC API以前,Microsoft的ODBC(开放数据库链接)API是用于访问关系数据库的最普遍使用的编程接口。

JDBC驱动程序实现因Java运行的各类操做系统和硬件平台而异,Sun将实现类型分为四种类型:

Type 1:JDBC-ODBC Bridge Driver

该类型驱动程序中,JDBC桥接器用于访问安装在每台客户机上的ODBC驱动程序。驱动把全部JDBC的调用传递给ODBC,再让后者调用数据库本地驱动代码(也就是数据库厂商提供的数据库操做二进制代码库,例如Oracle中的oci.dll),使用ODBC须要在系统上配置表示目标数据库的数据源名称(DSN)
当Java第一次出现时,这是一个有用的驱动程序,由于大多数的数据库只支持ODBC访问,但如今推荐这种类型的驱动程序仅用于实验用途或没有其余代替方案可用

image
image

优势:

  • 只要有对应的ODBC驱动(大部分数据库厂商都会提供),几乎能够访问全部的数据库。

缺点:

  • 执行效率比较低,不适合大数据量访问的应用
  • 因为须要客户端预装对应的ODBC驱动,不适合Internet/Intranet应用

Type 2:JDBC-Native API

在该类型驱动程序中,驱动经过客户端加载数据库厂商提供的本地代码库(C/C++等)来访问数据库,JDBC API调用将转换为本机C/C++ API调用,这些调用对于数据库是惟一的。与JDBC-ODBC Bridge的使用方式相同,必须在每台客户端计算机上安装供应商特定的驱动程序。
若是咱们更改数据库,咱们必须更改本机API,由于它是特定于数据库的并且如今几乎已通过时,可是你可能会经过该类型的驱动程序实现一些速度提高,由于它消除了ODBC的开销

image
image

优势:

  • 速度快于第一类驱动(但仍比不上第三、第4类驱动)。

缺点:

  • 因为须要客户端预装对应的数据库厂商代码库,仍不适合Internet/Intranet应用。

Type 3:JDBC-Net pure Java (网络协议)

此驱动程序给客户端提供了一个网络API,客户端上的JDBC使用套接字(Socket)来调用服务器上的中间件程序,该程序将JDBC调用转换为独立于DBMS的网络协议,而后由服务器将其转换为DBMS协议并转发到数据库服务器。这个网络服务器中间件可以将其纯Java客户端链接到许多不一样的数据库。使用的具体协议取决于供应商。一般,这是最灵活的JDBC替代方案,它不须要在客户端上安装代码。该解决方案的全部供应商均可能提供适合Intranet使用的产品。为了使这些产品也支持Internet访问,它们必须处理Web强加的安全性,经过防火墙访问等的附加要求。

image
image

能够将应用程序服务器视为JDBC“代理”,这意味着它会调用客户端应用程序。所以,须要了解应用程序服务器的配置,以便有效地使用此驱动程序。

优势:

  • 不须要在客户端加载数据库厂商提供的代码库,单个驱动程序能够对多个数据库进行访问,可扩展性较好。

缺点:

  • 在中间件层仍需对最终数据进行配置
  • 因为多出一个中间件层,速度不如第四类驱动程序

Type 4:Pure Java

这种类型的驱动程序将JDBC调用直接转换为DBMS使用的网络协议。这容许从客户端计算机直接调用DBMS服务器,是Intranet访问的绝佳解决方案。因为许多协议都是专有的,所以数据库供应商自己就是主要来源。

在Type 4驱动程序中,基于纯Java的驱动程序经过套接字链接与供应商的数据库直接通讯。这是数据库可用的最高性能驱动程序,一般由供应商本身提供。
这种驱动是很是灵活的,不须要在客户端或服务器上安装特殊的软件。此外,这些驱动程序能够动态下载。

image
image

优势:

  • 访问速度最快
  • 这是最直接、最纯粹的Java实现

缺点:

  • 几乎只有数据库厂商本身才能提供这种类型的JDBC驱动
  • 须要针对不一样的数据库使用不一样的驱动程序

MySql的 Connector/J 就是Type 4驱动程序。因为其网络协议的专有性质,数据库供应商一般提供Type 4驱动程序。

JDBC 体系结构

JDBC API包含两组主要的接口:第一组是应用程序编写者的JDBC API,第二组是驱动程序编写者的低级JDBC驱动程序API。应用程序和applet可使用基于纯Java JDBC技术的驱动程序经过JDBC API访问数据库,如图所示:

image
image

左侧Type 4,右侧Type 3

下图说明了使用ODBC驱动程序和现有数据库客户端库的JDBC链接:

image
image

左侧Type 1,右侧Type 2

应该使用哪一种驱动程序?

驱动类别|全Java|网络链接
-|-|-
JDBC-ODBC Bridge|No|Direct
Native API as basis|No|Direct
JDBC-Net|client Yes/server Maybe|Indirect
Native protocol as basis|Yes|Direct

  • Direct:JDBC客户端直接与DBMS服务器创建的链接,该服务器多是远程的
  • Indirect:JDBC客户端与中间件进程创建的链接,充当DBMS服务器的桥梁

驱动程序Type 3和4是使用JDBC API访问数据库的首选方法。驱动程序Type 1和2是临时解决方案,其中还没有提供直接纯Java驱动程序。对于须要中间件的Type 1和Type 2(下表中未显示)可能存在差别,但这些一般是不太理想的解决方案。Type 3和4提供了Java技术的全部优势

  • 若是要访问某种类型的数据库(如Oracle,Sybase或IBM),则首选Type 4驱动程序
  • 如股票Java应用程序同时访问多种类型的数据库,则Type 3是首选驱动程序
  • 当Type 3或Type 4驱动程序不可用的状况下采用Type 2驱动程序
  • Type 1驱动程序不被视为部署及驱动程序,一般仅用于开发和测试

JDBC-Datebase Connections

安装了适当的驱动程序后,就可使用JDBC简历数据库链接了.如下是四个基本步骤:

  • 导入JDBC包:须要包含数据库变成所需的JDBC类的包.大多数状况下,
    使用
    import sql.*
    就够了
  • 注册JDBC驱动程序:须要初始化驱动程序,将JVM所需的驱动程序实现加载到内存中,以即可以打开与数据库的通讯通道.
  • 打开一个链接: 须要使用DriverManager.getConneection()方法建立一个Conection对象,它表示与数据库的物理链接.
  • 执行查询:须要使用类型为Statement的对象来构建和提交SQL语句到数据库.
  • 从结果集中提取数据:须要使用相应的ResultSet.getXXX()方法从结果集中检索数据.
  • 清理环境: 须要明确的关闭全部数据库资源,而不依赖于JVM的垃圾回收.
导入JDBC包

import
语句告诉Java编辑器在哪里找到你在代码中引用的类的源代码位置.要使用标准JDBC包(容许在SQL表中选择,插入,更新和删除数据),请将如下导入添加到源代码中:

impor java.sql.*;
复制代码
注册JDBC驱动程序

在实用程序以前,必须先注册该驱动程序,将驱动程序的类文件加载到内存中,所以能够将其用做JDBC接口的实现。

Class.forName();

注册驱动程序最经常使用的方法是使用Java的Class.forName()将驱动程序的类文件动态加载到内存中,自动注册他。此方法是首选,由于它使驱动程序的注册可配置和可移植。

try{
    Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e){
    //
}
复制代码

注册驱动实际执行下面代码:

static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
}
复制代码
数据库URL配置

可使用DriverManager.getConnection() 方法创建链接。为了便于参考,这里列出三个重载的方法:

  • getConnection(String url)
  • getConnection(String url,Properties prop)
  • getConnection(String url,String user,String password)

每一个方法都须要一个数据库URL,下表列出了经常使用的JDBC、驱动程序名称和数据库URL。

RDBMS|JDBC驱动程序名称|网址格式
-|-|-
MySQL|com.mysql.jdbc.Driver|jdbc:myql://hostname/databaseName
ORACLE|oracle.jdbc.driver.OracleDriver|jdbc:oracle:thin:@hostname:port:databaseName
DB2|com.ibm.db2.jdbc.net.DB2Driver|jdbc:db2: hostname:port/databaseName
Sysbase|com.sybase.jdbc.SybDriver|jdbc:sybase:Tds: hostname:port/databaseName

URl中突出显示的部分都是静态的,只须要根据数据库设置更改剩余部分。

建立链接对象

数据库服务安装在本机,端口号为3306,用户名为root,密码为root,数据库名是test。

  • 使用带用户名和密码的数据库URL
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url,user,password);
复制代码
  • 仅使用数据库URL
String url = "jdbc:mysql://localhost:3306/test?user=root&password=root";
Connection connection = DriverManager.getConnection(url);
复制代码
  • 使用数据库URL和Properties对象
String url = "jdbc:mysql://localhost:3306/test";
Properties info = new Properties();
info.put("user","root");
info.put("password","root");
Connection connection = DriverManager.getConnection(url,info);
复制代码
关闭JDBC链接

在JDBC程序结束时,须要明确关闭全部的数据库链接。若是在程序中忘了关闭,Java垃圾回收器将在清理果实对象时关闭该链接。
可是依靠垃圾回收,特别是数据库编程,是一个很很差的编程实践。因此应该要养成使用与链接对象关联的close()方法关闭链接的习惯。
要确保链接已关闭,能够将关闭链接的代码编写在“finally”块中,无论是否发生异常“finally”块最后老是会执行。

connection.close();
复制代码

Statements、PreparedStatement和CallableStatement

一旦得到连接,咱们就能够与数据库进行交互。JDBC Statement,PreparedStatement和CallableStatement接口定义了可以发送SQL或PL/SQL命令以及从数据库接收数据的方法和属性。
他们还定义了有助于桥接数据库中使用的Java和SQL数据类型之间的数据类型差别的方法。
下表提供了每一个接口肯定要使用的接口的目的的摘要。

接口|推荐用途
-|-
Statement|用于对数据库的通用访问。在运行时使用静态SQL语句时颇有用。Statement接口不能接收参数。
PreparedStatement|当你计划屡次使用SQL语句时使用。PreparedStatement接口在运行时能够接收输入参数。
CallableStatement|在想要访问数据库存储过程时使用。CallableStatement接口也能够接受运行时输入的参数。

  1. Statement
Statement stmt = null;
try{
    stmt = connection.createStatement();
}catch(SQLException e){
    ...
}finally{
    stmt.close();
}
复制代码

在建立Statement对象后,可使用它来执行一个SQL语句,它有三个方法能够执行。分别是:

  • boolean execute(String sql):若是能够检索到ResultSet对象,则返回一个布尔值true,不然返回false。使用此方法执行SQL DDL语句或者须要使用真正的动态SQL,可使用执行建立数据库,建立表的SQL语句等等。
  • int executeUpdate(String sql):返回受执行SQL语句所影响的行数。使用次方法执行预期会影响多行的SQL语句,例如:INSERT、UPDATE或DELETE语句。
  • ResultSet executeQuery(String sql):返回一个ResultSet对象。当您西往得到结果集时,请使用此方法,就像使用SELECT语句同样。

关闭Statement对象

就像关闭Connection对象以保存数据库资源同样,出于一样的缘由,也应该关闭Statement对象。若是先关闭Connection对象,它也会关闭Statement对象。可是应该明确关闭Statement对象以确保正确的清理顺序。

  1. PreparedStatement对象

    PreparedStatement扩展了Statement接口,它提供了一些通用Statement额外的功能。这个语句具备动态提供参数的灵活性。

PreparedStatement pstmt = null;
try{
    String sql = "UPDATE Employees SET age = ? WHERE id = ?";
    pstmt = connection.prepareStatement(sql);
    pstmt.setInt(1,18);
    ...
}catch(SQLException e){
    ...
}finally{
    pstmt.close();
}
复制代码
  • JDBC中的全部参数都有‘?’符号做为占位符,这被称为参数标记。在执行SQL语句以前,必须为每一个参数(占位符)提供值。
    setXXX(int parameterIndex, XXX x) 方法将值绑定到参数,其中XXX表示绑定到输入参数的值的Java数据类型。若是忘记提供值,将抛出一个SQLException。
  • 每一个参数标记是它其位置序号的引用。第一个标记表示位置1,下一个位置2等等。该方法与Java数组下标不一样(从0开始)
  • 全部Statement对象与数据库交互的方法:execute(),executeQuery()和executeUpdate()也能够用于PreparedStatement对象。可是,这些方法被修改成可使用输入参数的SQL语句。
关闭PreparedStatement对象

若是先关闭Connection对象,也会关闭PreparedStatement对象。可是,应该明确的关闭PreparedStatement对象以确保正确顺序清理资源。

  1. CallableStatement对象
    相似Connection对象建立Statement和PreparedStatement对象同样,它还可使用桐言的方式建立CallableStatement对象,该对象将用于执行对数据库存储过程的调用。

假如须要执行如下MySQL存储过程:

DELIMITER $$

DROP PROCEDURE IF EXISTS 'EMP'.'getName' $$
CREATE PROCEDURE 'EMP'.'getName'
    (IN EMP_ID INT,OUT EMP_FIRST VARCHAR(255)
BEGIN 
    SELECT first INTO EMP_FIRST
    FROM Employees
    WHERE ID = EMP_ID;
END $$

DELIMITER ;
复制代码

存在三种类型的参数:IN,OUT和INOUT。PreparedStatement对象仅使用IN参数。CallableStatement对象可使用这三个。
如下是上面三种类型参数的定义:

参数|描述
-|-
IN|建立SQL语句是其参数值是未知的。使用setXXX()方法将值绑定到IN参数。
OUT|有SQL语句返回的参数值。可使用getXXX()方法从OUT参数中检索值。
INOUT|提供输入和输出值的参数。使用setXXX()方法绑定变量并使用getXXX()方法检索值。

CallableStatement cstmt = null;
try{
    String sql = "{call getEmpName (?,?)}";
    cstmt = connection.prepareCall(sql);
    ...
}catch(SQLException e){
    ...
}finally{
    cstmt.close();
}
复制代码

String变量sql表示存储过程,带有两个参数占位符。
使用CallableStatement对象就像使用PreparedStatement对象同样。在执行语句以前,必须将值绑定到全部参数。不然将抛出一个SQLException。
若是有IN参数,只需遵循适用于PreparedStatement对象的相同规则和技术。
使用OUt和INOUT参数时,必须使用一个额外的CallableStatement对象方法registerOutParameter()。registerOutParamter()方法将JDBC数据类型绑定到存储过程并返回预期数据类型。
当调用存储过程,可使用适当的getXXX()方法从OUT参数中检索该值,此方法将检索到的SQL类型的值转换为对应的Java数据类型。

关闭CallableStatement对象

就像关闭其余Statement对象同样,因为一样的缘由(节省数据库系统资源),还应该关闭CallableStatement对象。

简单的调用close()方法将执行关闭CallableStatement对象。 若是先关闭Connection对象,它也会关闭CallableStatement对象。 可是,应该始终显式关闭CallableStatement对象,以确保按正确顺序的清理资源。

JDBC-Result Sets

SQL语句执行后从数据库,返回的数据放在结果集中。SELECT语句用于从数据库中选择行并在结果集中查看它们的标准方法。java.sql.ResultSet接口表示数据库查询的结果集。

ResultSet对象维护指向结果集中当前行的游标。术语”结果集“指的是ResultSet对象中包含的行和列数据。

ResultSet接口的方法能够分为三类:

  • 导航方法:用于移动游标。
  • 获取方法:用于查看游标指向的当前行的列中的数据
  • 更新方法:用于更新当前行的列中的数据。而后,也能够在底层数据库中更新。

游标可根据ResultSet的属性移动。在建立生成ResultSet的对应Statement时,将指定这些属性。

JDBC提供如下连接方法来建立具备所需ResultSet的语句:

  • createStatement(int resultSetType,int resultSetConcurrency)
  • prepareStatement(String sql,int resultSetType,int resultSetConcurrency)
  • prepareCall(String sql,int resultSetType,int resultSetConcurrency)

resultSetType参数表示ResultSet对象的类型,resultSetConcurrency参数指定结果集是只读仍是可更新。

Type of ResultSet

能够指定的的resultSetType类型以下:(若是不指定任何resultSetType类型,将自动分配一个TYPE_FORWARD_ONLY值)

类型|描述
-|-
ResultSet.TYPE_FORWARD_ONLY|游标只能在结果集中向前移动
ResultSet.TYPE_SCROLL_INSENSITIVE|游标能够向前和向后滚动,结果集对建立结果集后发生的数据库所作的更改不敏感
ResultSet.TYPE_SCROLL_SENSITIVE|游标能够向前和向后滚动,结果集对建立结果集以后的其余数据库的更改敏感

Concurrency of ResultSet

能够制定的resultSetConcurrency类型以下:(若是不指定任何resultSetConcurrency类型,将自动分配一个CONCUR_READ_ONLY值)

并发性|描述
-|-
ResultSet.CONCUR_READ_ONLY|建立只读结果集,这是默认值。

ResultSet.CONCUR_UPDATABLE|建立可更新的结果集。

浏览结果集
方法|描述
-|-
public void beforeFirst() throw SQLException|将游标移动到第一行以前
public void afterLast() throw SQLException|将游标移动到最后一行以后
public boolean first() throw SQLException|将游标移动到第一行
public void last() throw SQLException|将游标移动到最后一行
public boolean absolute (int row) throw SQLException|将游标移动到指定行
public boolean relative (int row) throw SQLException|将游标从当前位置向前或向后移动给定的行数
public boolean previous () throw SQLException|将游标移动到上一行
public boolean next() throw SQLException|将游标移动到下一行
public int getRow() throw SQLException|返回游标指向的行号
public void moveToInsertRow () throw SQLException|将游标移动到结果集中的一个特殊行,该行可用于将新航插入数据库

public void moveToCurrentRow () throw SQLException|若是游标位于插入行,将光标移动到记住的光标位置,一般为当前行。若是光标不位于插入行上,则此方法无效。

查看结果集

每一个可能的数据类型都有get方法,每一个get方法都有两个版本:

  • 采用列名称
  • 采用列索引

方法|描述
-|-
public xxx getXXX(String columnName) throws SQLException|返回当前行名为columnName的列中值

public xxx getXXX(int columnIndex) throw SQLException|返回当前行中指定列索引的值。列索引从1开始,意味着行的第一列为1

更新结果集

ResultSet接口包含一组用于更新结果集的数据的更新方法。与get方法同样,每种类型都有两种:

  • 采用列名称
  • 采用列索引

例如,要更新结果集当前行的String例:
方法|描述
-|-
public void updateString(int columnIndex,String s) throw SQLException|将指定列中的String值更改成s的值
public void updateString(String columnName,String s) throw SQLException|将列名为columnName的String值更改成s的值

更新结果集中的行会更改ResultSet对象中当前行的列,但不会更改基础数据库中的列。要想对数据库中的行进行更改,须要调用如下方法:

方法|描述
-|-
public void updateRow()|经过更新数据库中相对应行来更新当前行
public void deleteRow()|从数据库中删除当前行
public void refreshRow()|刷新结果集中的数据以反映数据库中的任何最新更改
public void cancelRowUpdates()|取消对当前行进行的任何更新

public void insertRow()|在数据库中插入一行。只有在游标指向插入行时才能调用此方法。

数据类型

JDBC驱动程序将Java数据类型转换为适当的JDBC类型。而后将其发送到数据库。它为大多数数据类型提供并使用默认映射。例如:Java int类型会被转换成SQL INTERGER。建立默认映射以提供驱动程序之间的一致性

下表总结了在嗲用PreparedStatement或CallableStatement对象的setXXX()方法或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对象为每一个数据类型提供了相应的get方法来检索列值。每一个方法均可以使用列明或其序号位置来检索值。

SQL|JDBC/Java|getXXX
-|-|-
VARCHAR|java.lang.String|getString
CHAR|java.lang.String|getString
LONGVARCHAR|java.lang.String|getString
BIT|boolean|getBoolean
NUMERIC|java.math.BigDecimal|getBigDecimal
TINYINT|byte|getByte
SMALLINT|short|getShort
INTEGER|int|getInt
BIGINT|long|getLong
REAL|float|getFloat
FLOAT|float|getFloat
DOUBLE|double|getDouble
VARBINARY|byte[ ]|getBytes
BINARY|byte[ ]|getBytes
DATE|java.sql.Date|getDate
TIME|java.sql.Time|getTime
TIMESTAMP|java.sql.Timestamp|getTimestamp
CLOB|java.sql.Clob|getClob
BLOB|java.sql.Blob|getBlob
ARRAY|java.sql.Array|getARRAY
REF|java.sql.Ref|getRef

STRUCT|java.sql.Struct|getStruct

事务

若是JDBC链接处于自动提交模式(默认状况下),那么每一个SQL语句在完成后都会提交到数据库
对于简单的应用程序可能没问题,可是有三个缘由须要考虑是否关闭自动提交并管理本身的事务:

  • 提升性能
  • 保证业务流程的完整性
  • 使用分布式事务

事务可以控制什么时候将更改提交并应用于数据库。它将单个SQL语句或一组SQL语句视为一个逻辑单元,若是任何语句失败,则整个事务失败
要启动手动事务支持,需调用Connection对象的setAutoCommit()方法。若是将布尔值false传给setAutoCommit(),则关闭自动提交,也能够传递一个true来从新打开它

connection.setAutoCommit(false);
复制代码

提交和回滚

完成更改后,若要提交更改,那么可调用connection对象的commit()方法

connection.commit();
复制代码

若是要回滚数据库更新,使用如下代码:

connection.rollback();
复制代码

如下示例展现了若是使用提交和回滚:

try{
    connection.setAutoCommit(false);
    Statement stmt = connection.createStatement();
    String sql = "INSERT INTO Employees VALUES (123,'java'); stmt.executeUpdate(sql); connection.commit(); }catch(SQLException e){ connection.rollback(); }finally{ //release resource } 复制代码

Savepoint

新的JDBC 3.0添加了Savepoint接口提供了额外的事务控制能力。大多数现代DBMS支持其环境中的保存点,
设置Savepoint时,能够在事务中定义逻辑回滚点。若是在Savepoint以后发生错误,则可使用回滚方法来撤销全部更改或仅保存保存点以后所作的更改。
Connection对象由两个新方法管理保存点:

  • Savepoint setSavepoint(String name):定义新的保存点并返回该对象
  • void releaseSavepoint(Savepoint savepoint):删除保存点。
    有一个void rollback(Savepoint savepoint)方法,它将使用事务回滚到制定的保存点。
Savepoint savepoint = null;
try{
    connection.setAutoCommit(false);
    Statement stmt = connection.createStatement();
    String sql = "INSERT INTO Employees VALUES (123,'Java')";
    stmt.executeUpdate(sql);
    savepoint = connection.setSavepoint("Savepoint");
    sql = "INSERT INTO Employees VALUES (456,'C++')";
    stmt.executeUpdate(sql);
    sql = "INSERT INTO Employees VALUES ('789','Python')";//error sql
    stmt.executeUpdate(sql);
    connection.commit();
}catch(SQLException e){
    if (savepoint == null){
        connection.rollback();
    }else{
        connection.rollback(savepoint);
        connection.commit();    
    }
}finally{
    //release resource
}
复制代码

在这种状况下,第二条INSERT不会成功。

批量处理

批量处理容许将相关的SQL语句分组到批处理中,并经过对数据路的一次调用来提交它们。一次向数据库发送多个SQL语句,能够减小通讯开销,从而提升性能

  • 应使用DatabaseMetaData的supportsBatchUpdates()方法来肯定目标数据库是否支持批量更新。若是JDBC驱动程序支持此功能,该方法将返回true
  • Statement、PreparedStatement和CallableStatement的addBatch()方法用于将单个语句添加到批处理。
  • executeBatch()用于执行分在一组的全部语句并返回一个int数组,数组的每一个元素表示相应的更新语句的更新计数
  • clearBatch()方法能够删除使用addBatch()方法添加的全部语句。可是没法选择要删除某个语句

使用Statement对象进行批处理

  • 使用connection.createStatement()方法建立Statement对象
  • 使用connection.setAutoCommit(false)取消自动提交
  • 使用statement.addBatch(String sql)方法将SQL语句添加到批处理中
  • 使用statement.executeBatch()方法执行全部SQL语句
  • 使用connection.commit()方法提交全部更改
//create Statement object
Statement stmt = connection.createStatement();

//set auto-commit to false
connection.setAutoCommit(false);

//create some SQL statement
String sql1 = "INSERT INTO Employees VALUES (123,'Java')";
String sql2 = "INSERT INTO Employees VALUES (456,'C++')";

//add above SQL statement in the batch
stmt.addBatch(sql1);
stmt.addBatch(sql2);

//execute batch tasks
int[] result = stmt.executeBatch();

//commit transaction
connection.commit();
复制代码

使用PreparedStatement对象进行批处理

  • 建立带有占位符的SQL语句
  • 使用connection.prepareStatement()方法建立PreparedStatement对象
  • 使用connection.setAutoCommit(false)方法取消自动提交
  • 使用preparedStatement.addBatch(String sql)方法将SQL语句添加到批处理中
  • 使用preparedStatement.executeBatch()方法执行全部SQL语句
  • 使用connection.commit()方法提交全部更改
//create SQL Statement
String sql = "INSERT INTO Employees VALUES (?,?)";

//create PreparedStatement object
PreparedStatement pstmt = connection.prepareStatement(sql);

//set auto-commit to false
connection.setAutoCommit(false);

//set the variables
pstmt.setInt(123);
pstmt.setString("Java");
pstmt.addBatch();

 //set the variables
pstmt.setInt(456);
pstmt.setString("C++");
pstmt.addBatch();

//execute batch tasks
int[] result = pstmt.executeBatch();

//commit transaction
connection.commit();
复制代码

参考文献

Getting Started with the JDBC API:https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/GettingStartedTOC.fm.html

JDBC Tutorial:https://www.tutorialspoint.com/jdbc/index.htm

相关文章
相关标签/搜索