JDBC数据库访问技术
JDBC
封装了与底层数据库的通信细节,提供了与数据库相关的类和接口,为数据库开发人员提供了一种面向应用的开发平台。
第一节 JDBC概述
数据库是用于存储和处理数据的工具,数据库是构成了许多公司的重要基础。当前,由于数据库产品缤纷复杂,一个公司里经常出现同时使用多种数据库的现象。使用
Java
进行数据库开发时通过
JDBC
技术,可以一致性地访问不同的数据库,不用分别为不同平台的不同数据库编写各自不同的应用程序。
1、 JDBC的概念
JDBC(Java Data Base Connectivity
,
Java
数据库连接
)
是一种用于执行
SQL
语句的
Java API
,可以为多种关系数据库提供统一访问,它由一组用
Java
语言编写的类和接口组成。
JDBC
提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。同时,
JDBC
也是个商标名。
JDBC
向应用程序开发者提供了独立于数据库的、统一的
API
,这个
API
提供了编写的标准,并考虑了所有不同应用程序设计的标准,其关键是一组由驱动程序实现的
Java
接口。驱动程序负责标准的
JDBC
调用,当应用程序被移植到不同的平台或数据库系统,应用程序不变,改变的是驱动程序,驱动程序扮演了多层数据库设计中的中间层的角色。如下图所示:
应用程序
|
JDBC
|
Oracle
驱动程序
|
Sybase
驱动程序
|
DB2
驱动程序
|
Informix
驱动程序
|
……
驱动程序
|
Java
具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特征,是编写数据库应用程序的杰出语言,所需要的只是
Java
应用程序与各种不同数据库之间进行对话的方法,而
JDBC
正是作为此种用途的机制。
随着越来越多的使用
Java
编程语言,对从
Java
中便捷的访问数据库的要求也在日益增加。对于服务器端的数据库应用,
JDBC
往往与
RMI
、
CORBA
、
JSP
或
Servlet
,
EJB
等技术混合使用,用于实现多层结构的应用系统。
2、 数据库驱动程序
数据库厂商一般会提供一组
API
访问数据库。流行的数据库如
Oracle
、
SQL Server
、
Sybase
和
Informix
都为客户提供了专用的
API
。有四种类型的数据库驱动程序,它们分别是:
u
JDBC
-
ODBC
桥
u
部分
Java
、部分本机驱动程序
u
中间数据访问服务器
u
纯
Java
驱动程序
以下分别介绍。
JDBC-ODBC桥
JDBC
-
ODBC
桥是一个
JDBC
驱动程序,它通过将
JDBC
操作转换为
ODBC
操作来实现
JDBC
操作。对
ODBC
,它像是通常的应用程序,桥为所有对
ODBC
可用的数据库实现
JDBC
。它作为
sun.jdbc.odbc
包实现,其中包含一个用来访问
ODBC
的本地库。桥是由
Intersolv
和
JavaSoft
联合开发的。由于
ODBC
被广泛地使用,该桥地优点是让
JDBC
能够访问几乎所有地数据库。
通过
odbc
子协议,使用
URL
打开
JDBC
连接即可使用桥。建立连接前,必须将桥驱动程序类
sun.jdbd.odbc.JdbcOdbcDriver
添加到名为
jdbc.drivers
的
java.lang.System
属性中,或用
Java
类加载器将其显示的加载。可以用如下方式显示加载:
Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”)
;
加载时,
ODBC
驱动程序将创建自己的实例,同时在
JDBC
驱动程序管理器中进行注册。
桥驱动程序使用
odbc
子协议。该子协议的
URL
为以下的形式:
jdbc:odbc:<data-dource-name>[<attribute-name>=<attribute-value>]*
JDBC
-
ODBC
桥在
JDBC API
和
ODBC API
之间提供了一个桥梁,这个桥把标准的
JDBC
调用翻译成对应的
ODBC
调用,然后通过
ODBC
库把它们发送到
ODBC
数据源,如图所示:
JDBC
API
|
ODBC
API
|
JDBC-ODBC桥
|
ODBC层
|
数据源
|
Java应用程序
|
这种方式有一个明显的缺点就是效率相对低下,所以不推荐使用这种桥驱动程序,但它可以减少开发人员进行企业开发的麻烦。
部分Java、部分本机驱动程序
这种驱动使用
Java
实现与数据库厂商专有
API
的混和形式来提供数据访问。它比前一种方式要快。
JDBC
驱动将标准的
JDBC
调用转变为对数据库
API
的本地调用,该类型的驱动程序是本地部分
Java
技术性能的本机
API
驱动程序,如下图所示:
JDBC
API
|
厂商专有API
|
JDBC驱动程序
|
数据源
|
Java应用程序
|
在这种方式里,驱动程序和厂商专有的
API
必须在每个运行
Java
应用程序的客户端安装。
现在大多数的数据库厂商都在其数据库产品中提供驱动程序,这种使用方式比前一种有效。
中间数据访问服务器
这种方式使用一个中间数据访问服务器,通过这种服务器,它可以把
Java
客户端连接到多个数据库服务器上,如下图所示:
JDBC
API
|
JDBC驱动程序
|
本机驱动程序
|
数据源
|
Java应用程序
|
JDBC驱动程序
|
这种方式不需要客户端的数据库驱动,而是使用网络-服务器中间层来访问一个数据库。该类型的驱动程序是网络协议完全
Java
技术性能的驱动程序,它为
Java
应用程序提供了一种进行
JDBC
调用的机制。
使用该类型的驱动程序是平台无关的,并且不需要客户端的安装和管理,因此很适合做
Internet
的应用。
纯Java驱动程序
这种方式使用厂商专有的网络协议把
JDBC API
调用转换成直接的网络调用,这种方式的本质是使用套接字(
Socket
)进行编程。纯
Java
驱动运行在客户端,并且直接访问数据库,因此运行这个模式要使用一个两层的体系,如下图所示:
JDBC
API
|
JDBC驱动程序
|
数据源
|
Java应用程序
|
该类型的驱动程序是本地协议完全
Java
技术性能的驱动程序,同时它的使用也比较简单,客户端不需要安装任何中间件或者运行库。现在大部分厂商都提供第四类驱动程序的支持。
3、 JDBC的用途
Ø
与数据库建立连接
Ø
发送
SQL
语句
Ø
处理结果
4、 JDBC URL
JDBC URL的概念
JDBC URL
提供了一种标识数据库的方法,可以使相应的驱动程序能识别该数据库并与之建立连接。
JDBC
的作用是提供某些约定,驱动程序编程员在构造它们的
JDBC URL
时应该遵循这些约定。
u
由于
JDBC URL
要与各种不同的驱动程序一起使用,因此,这些约定应非常灵活。首先,它们应允许不同的驱动程序使用不同的方案来命名数据库。
u
JDBC URL
应允许驱动程序编程员将一切所需的信息编入其中。
u
JDBC URL
应允许某种程度的间接性。即:
JDBC URL
可指向逻辑主机或数据库名,而这种逻辑主机或数据库名将由网络命名系统动态的转换为实际的名称。
JDBC URL的语法格式
JDBC URL
的标准语法如下所示。它由三部分组成,各部分之间用冒号分割。
jdbd:<
子协议
>:<
子名称
>
JDBC URL
的三个部分可分解如下:
Ø
jdbc
:协议,
JDBC URL
中的协议总是
jdbc
。
Ø
<
子协议
>
:驱动程序名或数据库连接机制的名称。
Ø
<
子名称
>
:标识数据库的方法。子名称可以依不同的子协议而变化,使用子名称的目的是为定位数据库提供足够的信息。如果数据库是通过
Internet
来访问的,则在
JDBC URL
中应将网络地址作为子名称的一部分包括进去,且必须遵循如下所示的标准
URL
命名约定:
//
主机名:端口
/
子协议
对于不同的数据库,厂商提供的驱动程序和连接的
URL
都不同,几个主要的数据库厂商与其对应的驱动程序和连接的
URL
如下所示:
数据库驱动程序和URL
数据库名
|
驱动程序
|
URL
|
MS SQL Server 2000
|
com.microsoft.jdbc.sqlserver.SQLServerDriver
|
Jdbc:Microsoft:sqlserver://[ip]:[port];user=[user];password=[password]
|
JDBC-ODBC
|
sun.jdbc.odbc.JdbcOdbcDriver
|
jdbc:odbc:[odbcsource]
|
Oracle oci8
|
orcle.jdbc.driver.OracleDriver
|
jdbc.oracle:oci8:@[sid]
|
Oracle thin Driver
|
oracle.jdbc.driver.OracleDriver
|
jdbd:oracle:thin:@[ip]:[port]:[sid]
|
Cloudscape
|
com.cloudscape.core.JDBCDriver
|
jdbc:cloudscape:database
|
MySQL
|
org.gjt.mm.mysql.Driver
|
jdbc:mysql:/ip/database,user,password
|
5、 ODBC子协议
子协议
odbc
是一种特殊情况。它是为指定
ODBC
风格的数据资源的
URL
而保留的,并允许在子名称后面指定任意多个属性值。
odbc
子协议的完整语法为:
jdbc
:
odbc
:
<
数据资源名称
>[;<
属性名
>=<
属性值
>]*
例如:
jdbc:odbc:test
jdbc:odbc:test;UID=uid;PWD=pwd
6、事务
事务就是将一些
SQL
语句作为一个整体来执行,要么所有语句全部完成,要么一条语句都不执行。当调用方法
commit
或
rollback
时,当前事务即告结束,另一个事务谁即开始。
缺省情况下,新连接处于自动提交模式,也就是说,当执行完语句后,将自动对那个语句调用
commit
方法。这种情况下,由于每个语句都是被单独提交的,因此,一个事务只由一个语句组成。如果禁用自动提交模式,事务将要等到
commit
或
rollback
方法被显示调用时才结束,因此,它将包括上一次调用
commit
或
rollback
方法以来所有执行过的语句。对于第二种情况,事务中的所有语句将作为组来提交或还原。
方法
commit
使
SQL
语句对数据库所做的任何更改成为永久性的,它还将释放事务持有的全部锁,而方法
rollback
将放弃那些更改。
第二节 JDBC核心类和接口
Driver接口
每个数据库驱动程序必须实现
Driver
接口。我们在编程中要连接数据库,必须先装载特定厂商提供的数据库驱动程序(
Driver
),驱动程序的装载方法如下:
Class.forName(“<driver_class_name”>);
DriverManager类
对于简单的应用程序,仅需要使用该类的方法
getConnection
,该方法将建立与数据库的连接。
JDBC
允许用户调用
DriverManager
类的方法
getDriver
、
getDrivers
和
registerDriver
及
Driver
类的方法
connect
,但在多数情况下,最好让
DriverManager
类管理建立连接的细节。
DriverManager
类是
JDBC
的管理层,作用于用户和驱动程序之间,用于管理
JDBC
驱动程序。它跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接,另外,
DriverManager
类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。
作为初始化的一部分,
DriverManager
将试图装载系统属性“
jdbc.drivers
”所指的驱动程序类,这可以让用户定制在它们的应用程序中使用的
JDBC
驱动程序。例如,在
Windows
的
HOME
目录的
/.hotjava/properties
文件中,可以指定:
Jdbc.drivers=<
驱动程序
>
另外,程序还可以显示的装载
JDBC
驱动程序。例如:下述代码用于装载
my.sql.Driver
Class.forName(“my.sql.Driver”);
在
DriverManager
中有一个非常重要的方法,就是
getConnection(parameter)
,通过这个方法可以获得一个连接。
getConnection
public static synchronized Connection getConnection(String url)
尝试建立一个和给定的
URL
的数据库连接,调用此方法时,
DriverManager
将在已注册的驱动中选择恰当的驱动来建立连接。
public static synchronized Connection getConnection(String url, Properties info)
与上类似,不过提供了一些属性,这些属性连接特定的数据库需要,至少包含
user
和
password
属性。
public static synchronized Connection getConnection(String url, String user,String password)
连接到指定
URL
的数据库,使用用户名为
user
,密码为
password
。
Connection接口
Connection
对象代表与数据库的连接,也就是在已经加载的
Driver
和数据库之间建立连接。
连接过程包括所执行的
SQL
语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。
close
public void close()
关闭到数据库的连接,在使用完连接之后必须关闭,否则连接会保持一段比较长的时间,直到超时。
commit
public void commit()
提交对数据库的更改,使更改生效。这个方法只有调用了
setAutoCommit(false)
方法后才有效,否则对数据库的更改会自动提交到数据库。
createStatement
public Statement createStatement()
throws SQLException
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException
public Statement createStatement(int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException
创建一个
Statement
,
Statement
用于执行
SQL
语句。
prepareStatement
public PreparedStatement prepareStatement(String sql)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int autoGeneratedKeys)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int[] columnIndexes)
throws SQLException
public PreparedStatement prepareStatement(String sql,
String[] columnNames)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int resultSetType,
int resultSetConcurrency)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException
public PreparedStatement prepareStatement(String sql,
String[] columnNames)
throws SQLException
使用指定的
SQL
语句创建一个预处理语句,
sql
参数中往往包含一个或者多个“?”占位符。
Statement接口
一个
Statement
对象仅能有一个
ResultSet
对象。当完成对表的操作之后,必须重新得到另一个
Statement
对象,才能进行其他表的操作。
Statement
对象可以用于执行一个静态的
SQL
语句,并得到
SQL
语句执行后的结果。
execute
public boolean execute(String sql)
throws SQLException
运行语句,返回是否有结果集。
executeQuery
throws SQLException
运行查询语句,返回ResultSet对象。
executeUpdate
public int executeUpdate(String sql)
throws SQLException
运行更新操作,返回更新的行数。
PreparedStatement接口
PreparedStatement
对象可以代表一个预编译的
SQL
语句,它从
Statement
接口继承而来,并与之有如下两方面的不同:
u
PreparedStatement
实例包含已编译的
SQL
语句。
u
包含于
PreparedStatement
对象中的
SQL
语句可具有一个或多个
IN
参数。
IN
参数的值在
SQL
语句创建时未被指定。相反的,该语句为每个
IN
参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的
setXXX
方法来提供。
由于
PreparedStatement
对象已经预编译过,所以其执行速度要快于
Statement
对象。因此,需要多次执行的
SQL
语句经常创建为
PreparedStatement
对象,以提供效率。
作为
Statement
的子类,
PreparedStatement
继承了
Statement
的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代
IN
参数占位符的值。
另外,
Statement
接口中的三种方法
execute
、
executeQuery
和
executeUpdate
已经被更改,使之不再需要参数。
所有的
setXXX
方法的原型如下,其中
XXX
是与该参数相应的类型。例如,如果参数具有
Java
类型
long
,则使用的方法就是
setLong
。
setXXX
方法的第一个参数
parameterIndex
为要设置的参数的序数位置,第二个参数是设置给该参数的值。
setXXX
public void setXXX(int parameterIndex, XXX x)
throws SQLException
设定指定位置的参数值。
execute
public boolean execute()
throws SQLException
运行语句,返回是否有结果集。
executeQuery
public ResultSet executeQuery()
throws SQLException
运行查询语句,返回ResultSet对象。
executeUpdate
public int executeUpdate()
throws SQLException
运行更新操作,返回更新的行数。
ResultSet接口
ResultSet
提供了对数据库中表的访问,一个
ResultSet
对象通常由执行一个
Statement
而生成。
ResultSet
对象维护了一个光标指针,用来描述当前记录的位置,最初,光标指向第一个记录的位置之前,
next
方法将光标移到到下一个记录,其方法的原型如下。
boolean next() throws SQLException
getXXX
方法得到当前记录的字段的值,可以使用字段名或其索引得到自动的值,一般来说,使用字段的索引效率更高,字段的编号从
1
开始,
1
为当前记录的第一个字段,
2
为第二个字段,以此类推。
在
ResultSet
接口中,还有一系列的
updateXXX
方法,可以用来更新当前记录的字段值。
getXXX
public XXX getXXX(int parameterIndex)
throws SQLException
public XXX getXXX(String columnName)
throws SQLException
获取指定位置或者指定列的查询结果值。
第三节 JDBC编程实例
基本步骤
Ø
输入
java.sql
包
在程序的开头,必须加入下面的代码:
Import java.sql.*;
Ø
声明变量
在代码中,一般要声明三个相关的变量。
Stmt
用于
SELECT
语句,
pstmt
用于
UPDATE
语句,
rs
用于
SELECT
的结果集。
Statement stmt
;
PreparedStatement pstmt
;
ResultSet rs
;
Ø
加载
jdbc
驱动程序
Class.forName(“<jabc
驱动程序
>”);
Ø
定义
JDBC URL
String url = “jdbc:odbc:oracle:thin@10.10.10.1:db9”;
Ø
连接数据库
Connection conn = DriverManager.getConnection(url);
Ø
进行相应的数据操作
根据需要创建
Statement
或
PreparedStatement
实例,执行
SELECT
操作或
UPDATE
操作。
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");
Ø
关闭数据库连接
conn.close();
查询记录信息
Vector data = new Vector();
ResultSet rs = null;
Statement stmt = null;
Connection conn = null;
//
注册驱动器
try
{
//
加载驱动程序
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//Class.forName("oracle.jdbc.driver.OracleDriver");
//
得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
//
执行
sql
语句
stmt = conn.createStatement();
//
返回数据集合
rs = stmt.executeQuery("select * from book where bookid=‘0007’");
while (rs.next())
{
DataObject dataObject = new DataObject();
dataObject.setBookId(rs.getString("bookid"));
dataObject.setBookName(rs.getString("bookname"));
dataObject.setBookNumber(rs.getString("booknumber"));
data.addElement(dataObject);
}
}
catch (SQLException e1)
{
e1.printStackTrace();
}
catch (ClassNotFoundException e)
{
System.out.println("Driver not found");
}
//
关闭联接
finally
{
try
{
rs.close();
stmt.close();
conn.close();
}
catch (SQLException e2)
{
e2.printStackTrace();
}
}
插入记录信息
String sql;
Connection conn = null;
PreparedStatement ps=null;
//
注册驱动器
try
{
//
加载驱动程序
Class.forName("oracle.jdbc.driver.OracleDriver");
//
得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
//
执行
sql
语句
sql="insert into book ( bookid,bookname,booknumber) values (?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "0021");
ps.setString(2, "
数据库概论
");
ps.setInt(3, 11);
ps.executeUpdate();
}
catch (SQLException e1)
{
e1.printStackTrace();
}
catch (ClassNotFoundException e)
{
System.out.println("Driver not found");
}
//
关闭联接
finally
{
try
{
ps.close();
conn.close();
}
catch (SQLException e2)
{
e2.printStackTrace();
}
}
更新记录信息
int state;
Statement stmt = null;
Connection conn = null;
//
注册驱动器
try
{
//
加载驱动程序
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//Class.forName("oracle.jdbc.driver.OracleDriver");
//
得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
//
执行
sql
语句
stmt = conn.createStatement();
//
返回数据集合
state=stmt.executeUpdate("update book set booknumber=9 where bookname='java
编程思想
'");
if(state>0)
{
System.out.println("
更新成功
");
}
else
{
System.out.println("
更新失败
");
}
}
catch(SQLException e1)
{
e1.printStackTrace();
}
catch(ClassNotFoundException e)
{
System.out.println("Driver not found");
}
//
关闭联接
finally
{
try
{
stmt.close();
conn.close();
}
catch(SQLException e2)
{
e2.printStackTrace();
}
}
删除记录信息
boolean state=false;
Statement stmt = null;
Connection conn = null;
//
注册驱动器
try
{
//
加载驱动程序
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//Class.forName("oracle.jdbc.driver.OracleDriver");
//
得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
//
执行
sql
语句
stmt = conn.createStatement();
//
返回数据集合
state=stmt.execute("delete from book where bookid='0007'");
if(state)
{
System.out.println("
删除成功
");
}
else
{
System.out.println("
删除失败
");
}
}
catch (SQLException e1)
{
e1.printStackTrace();
}
catch(ClassNotFoundException e)
{
System.out.println("Driver not found");
}
//
关闭联接
finally
{
try
{
stmt.close();
conn.close();
}
catch (SQLException e2)
{
e2.printStackTrace();
}
}
第四节 JDBC数据库连接池
在实际应用开发中,特别是在
Web
应用系统中,如果
JSP
、
Servlet
或
EJB
使用
JDBC
直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等过程,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁的发生这种数据库操作,系统的性能必然会急剧的下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法,在许多应用程序服务器中,基本都提供了这项技术,无需自己编程。
数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个
vector
对象中,一旦数据库连接建立后,不同的数据库访问请求就可以共享这些连接,这样,通过复用这些已建立的数据库连接,可以克服上述缺点,极大的节省了系统资源和时间。
数据库连接池的主要操作如下:
Ø
建立数据库连接池对象
Ø
按照事先指定的参数创建初始数量的数据库连接
Ø
对于一个数据库访问请求,直接从连接池中得到一个连接。如果数据库连接池对象中没有空闲的连接,且连接数没有达到最大,创建一个新的数据库连接
Ø
存取数据库
关闭数据库,释放所有数据库连接。