转载自:http://danielzzu.blog.163.com/blog/static/11851530420109112142744/
JDBC是一种用于执行SQL语句的Java API ,JDBC本身是一个商标名,而不是一个缩写字;然而,JDBC通常被认为是代表“Java数据库连接 Java database connectivity ”。它由一组用Java编程语言编写的类和接口组成,JDBC为工具/数据库开发人员提供了一个标准的API,使他们能够用纯Java API 来编写数据库应用程序。
---------------------------------------------------传说中的分割线---------------------------------------------------------
JDBC入门:
一、概述JDBC
JDBC从物理结构上说就是Java语言访问数据库的一套接口集合。
从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议。
JDBC API 使得开发人员可以使用纯Java的方式来连接数据库,并进行操作。
ODBC:基于C语言的数据库访问接口。
JDBC:是Java版的ODBC。
JDBC 特性:高度的一致性、简单性(常用的接口只有4、5个)。
驱动程序按照工作方式分为四类:
1、JDBC-ODBC bridge + ODBC 驱动
JDBC-ODBC bridge桥驱动将JDBC调用翻译成ODBC调用,再由ODBC驱动翻译成访问数据库命令。
优点:可以利用现存的ODBC数据源来访问数据库。
缺点:从效率和安全性的角度来说的比较差。不适合用于实际项目。
2、基于本地API的部分Java驱动
我们应用程序通过本地协议跟数据库打交道。然后将数据库执行的结果通过驱动程序中的Java部分返回给客户端程序。
优点:效率较高。
缺点:安全性较差。
3、纯Java的网络驱动
(中间协议) (本地协议)
app JDBC 纯Java 中间服务器 DB
缺点:两段通信,效率比较差
优点:安全信较好
4、纯Java本地协议:通过本地协议用纯Java直接访问数据库。
特点:效率高,安全性好。
二、JDBC应用开发模型
1、两层结构
在这种结构模型下,应用程序(客户端)直接与数据库(服务器)相连
客户端<-----------Database Driver-------------->数据库服务器
2、三层结构
唉三层结构模型中,客户端和数据库服务器之间增加了一个中间服务器,客户机与中间服务器进行通信,由中间服务器处理客户机的请求并控制对一个或多个数据库管理系统的访问控制。
客户端<--------------------->中间服务器<---------------------->数据库服务器
---------------------------------------------------传说中的分割线---------------------------------------------------------
JDBC典型的实例-----------------
使用JDBC进行数据库开发,最常用的对象就是Connection 、Statement 、和 ResultSet ,典型的开发过程如下:
1、创建一个连接
Connection 对象代表与数据库的连接,连接过程包括所执行的SQL语句和在该连接上所返回的结果,一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。
下述代码显示了 如何打开一个URL的数据库的连接,所用的用户名为sa,密码为sa
Connection conn = DriverManager.getConnection("192.168.0.1:8000:database","sa","sa");
2、执行SQL语句
连接一旦成立,就可以用来向它所涉及的数据库传送SQL语句。
jdbc的Statement接口提供了向数据库发哦那个指令的切入点:
Statement stmt = conn.createStatement();
得到了Statement对象,你就可以向数据库发送SQL指令了:
stmt.executeUpdate("insert into test_table value(1,2......)");
(注:
executeUpdate(String sql)
执行给定 SQL 语句,该语句可能为INSERT、UPDATE或DELETE语句,或者不返回任何内容的 SQL 语句(如 SQL DDL 语句)。)
3、查询结果集
一条典型的SQL查询指令会返回一个ResultSet对象,ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get( )方法(这些get( )方法可以访问当前行中的不同列)提供了对这些行中数据的访问。ResultSet.next()方法用于移动到ResultSet中的下一行,使下一行成为当前行。
ResultSet rs = stmt.executeQuery("select a,b,c from test_table");
while(rs.next()){
System.out.println(rs,getString(1));
System.out.println(rs,getString(1));
System.out.println(rs,getString(1));
}
4、最后的工作
在使用完以上3个对象后,必须逆顺序依次关闭它们:
rs.close();
stmt.close();
conn.close();
---------------------------------------------------传说中的分割线---------------------------------------------------------
OK,下面给出一个完整的连接数据的简单案例:
imp
imp
imp
imp
public class TestJDBC {
public static void main(String[] args) {
//声明Connection引用
Connection conn =null;
try{
//加载JDBC-ODBC桥驱动类
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//创建数据库连接
conn=DriverManager.getConnection("jdbc:odbc:stu","","");
//创建Statement对象
Statement stmt = conn.createStatement();
//在数据库中插入一条记录
int count = stmt.executeUpdate("insert into student values(2005254218,'wgy',23,'1234567')");
//将所有学生的年龄加一岁
count=stmt.executeUpdate("update student set sage=sage+1");
System.out.println("成功更新了"+count+"条记录!!!");
//对student进行全表检索
ResultSet rs = stmt.executeQuery("select * from student");
//打印表头信息
System.out.println("===================================");
System.out.println("学号\t姓名\t年龄\t电话");
//循环打印结果集中的每一条记录
while(rs.next()){
//获得当前记录中的第一列内容
String sid = rs.getString(1);
//获得当前记录中的第二列内容
String sname = rs.getString(2);
//获得当前记录中的第三列内容
String sage = rs.getString(3);
//获得当前记录中的第四列内容
String stel = rs.getString(4);
//打印本条记录的内容
System.out.println(sid+"\t"+sname+"\t"+sage+"\t"+stel);
}
//关闭结果集
rs.close();
//关闭语句
stmt.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//关闭数据库连接
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
在讨论JDBC核心类之前,先把 JDBC编程的步骤给出,边看边理解,我是这样学习的。
JDBC的编程步骤:
imp
1.参数化
String driverName = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test"; //协议;库或服务器名称;服务器IP,端口
String username = "root";
String password="";
/* Oracle的连接
String driverName = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@192.168.0.23:1521:ora10g";
String username = "openlab";
String password="open123";*/
/* SQL Server的连接
String driverName = "com.microsoft.jdbc.SQL Server.SQL ServerDriver";
String url = "jdbc:microsoft:SQL Server://localhost:1433:DatabaseName=test";
String username = "openlab";
String password="open123";*/
//以下这些都需要写在有异常的代码块里,所以需要提取出来。
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;//建议用PreparedStatement
2.加载和注册数据库驱动
Class.forName(driverName);//自动注册;需要把驱动的jar包导进来;需处理异常
/*方法二:实例化具体的Driver驱动,这写法一般不用(不能参数化驱动名,不够灵活)
Driver driver = new com.mysql.jdbc.Driver();
DriverManager.registerDriver(driver); //将驱动交于DriverManager托管*/
/*方法三:Dos运行时,java -Djdbc.drives = oracle.jdbc.driver.OracleDriver; --可多个 */
3.连接数据库
conn = DriverManager.getConnection(url, username, password);//需处理异常
//Connection返回数据库连接,如:“com.mysql.jdbc.Connection@1ffb8dc”;连接不成功则返回 null
4.创建Statement对象 //为了类型安全和批量更新的效率,改用PreparedStatement
stmt = conn.createStatement();//需处理异常
//返回其生成结果的对象"oracle.jdbc.driver.OracleStatement@198dfaf"
5.操作数据库,执行SQL语句
String sql = "select * from tableName";//SQL语句里不需要写分号
rs = stmt.executeQuery(sql); //executeQuery(sqlString) 查询 返回查询结果集
/* String sql = "insert into tableName values(?,?)"; // ?占位符
int number = stmt.executeUpdate(sql);//更新,再返回int(更新、修改影响的条数) */
6.处理数据(游标)
StringBuffer sb = new StringBuffer(); //缓存;用它可提高读取速度。当然,不用也可以。
ResultSetMetaData md = rs.getMetaData(); //ResultSetMetaData可获取列的类型和属性信息
int col = md.getColumnCount(); //获取列的数目
while(rs.next()){ //rs.next()使游标下移一位,返回boolean,没有下一个结果则返回false
for(int i=1; i<=col;i++){ // index(JDBC 的下标从1开始)
sb.append(md.getColumnName(i)+"="+rs.getString(i)+" ");
} sb.append("\n");
}System.out.println(sb);
//1.游标的初始位置在第一条记录的前面,使第一次调用next()后,刚好拿到第一个结果。
//2.游标的最终位置在最后一条记录的后面(结果集的前面和后面留空,真正存在)
7.释放资源,断开与数据库的连接
//先判断是否有引用资源,再释放(释放空资源会抛异常);注意顺序
if(rs!=null)try{rs.close();}catch(Exception e){e.printStackTrace();}
if(stmt!=null)try{stmt.close();}catch(Exception e){e.printStackTrace();}
if(conn!=null)try{conn.close();}catch(Exception e){e.printStackTrace();}
//这些异常没法处理,处理只为方便调试。所以这些异常处理也只是打印。
/*要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,
因为ResultSet需要Statement和Connection连接时才可以用的;Statement也需要Connection才可用;
结束Statement之后有可能其它的Statement还需要连接,因此不能先关闭Connection。ResultSet同理。*/
步骤 2、3、7 每次都一样,可以重构。
因为加载驱动是个一次性工作,所以可以采用静态初始化块来加载驱动;
连接数据库的方法应该自己负责,获取数据库连接信息和驱动的信息,并处理相关异常;
释放数据库资源的方法要考虑到ResultSet、Statement、Connection的不同情况,并处理相关异常。
-----------------------------------------------传说中的分割线----------------------------------------------------------------
简单的说,JDBC可以做三件事:
一、与数据库建立连接
二、发送SQL语句
三、处理结果
下面的代码段给出了上面3步的基本示例:
Connection conn = DriverManager.getConnection("jdbc:odbc:datasource","username","password");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select a,b,c from table2");
while(rs.next())
System.out.println(rs,getString("a")+rs.getString("b")+rs.getString("c"));
上面这段程序解释下:
Java.sql.DriverManager :用来加载不同的JDBC驱动程序并且为创建新的数据库连接提供支持。
Java.sql.Connection :完成对某一指定数据库的连接功能。
Java.sql.Statement :在一个已经创建的连接(Java.sql.Connection)中作为执行SQL语句的容器,它包含了如下两个重要的子类:
1、Java.sql.PreparedStatement :用于执行预编译的SQL语句。
2、java.sql.CallableStatement :用于执行数据库中已经创建好的存储过程(StoredPROCEDURE)
java.sql.ResultSet :代表指定SQL语句执行后的数据库结果集。
(注:以上这些类是编写JSP程序经常要调用的,关系图如下:)
|Connection<----------->Statement
|Connection<----------->Statement<----------->ResultSet
|
DriverManager<--->|<----------->Connection<----------->PreparedStatement<----------->ResultSet
|
|<----------->Connection<----------->CallableStatement
-----------------------------------------------传说中的分割线----------------------------------------------------------------
下面再将一些细节给总结一下
通过 Connection 对象建立数据库连接,各种数据库的 URL 如下:
MySQL :jdbc:mysql://localhost:3306/test 3306是端口号,test是数据库名称,根据自己实际的改,以下也是!
Oracle :jdbc:oracle:thin:@192.168.0.23:1521:ora10g
SQL Server :jdbc:microsoft:SQL Server://localhost:1433:DatabaseName=test
注意,在获得连接之前,必须先注册驱动程序。通过方法Class.forName
ODBC :Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
MySQL :Class.forName("com.mysql.jdbc.Driver");
Oracle :Class.forName("oracle.jdbc.driver.OracleDriver");
SQL Server :Class.forName("com.microsoft.jdbc.SQL Server.SQL ServerDriver");
-----------------------------------------------传说中的分割线----------------------------------------------------------------
通过 Connection 取得连接,然而,该对象还可以通过以下的方法进行事物管理(会在后面详细说明,这里只是给出个第一印象)
1、设置不自动提交
conn.setAutoCommit(false);
2、提交数据
conn.commit();
3、回滚数据
conn.rollback();
4、设置自动提交
conn.setAutoCommit(true);
-----------------------------------------------传说中的分割线----------------------------------------------------------------
有时候我们会在开发当中遇到这种情况,我们并未被告知所使用的数据库是什么类型的,那我们该怎么办呢?
使用 DatabaseMetaData 查看数据库信息。
下面的语句可以在一个已经初始化的 Connection 对象的基础上创建一个 DatabaseMetaData 对象:
DatabaseMetaData datameta = conn.getMetaData();
下面给出一些常用的方法,其他方法可以参考API中的 DatabaseMetaData 类
在这些方法中,有些驱动程序可能并不支持,这时会产生一个SQL Exception 异常,异常中会给出驱动程序不支持该方法的信息。
一、获得数据库的基本信息
1、getURL() 返回一个String类对象,代表数据的URL
2、getUserName() 返回连接当前数据库管理系统的用户名
3、isReadOnly() 返回一个Boolean 值,指示该数据库是否只允许 读 操作
4、getDatabaseProductName() 返回数据库的产品名称
5、getDatabaseProductVersion() 返回数据库的版本号
6、getDriverName() 返回驱动程序的名称
7、getDriverVersion() 返回驱动程序的版本号
二、获得数据库中各个表的情况
ResultSet getTables(String catalog,String schemaPattem,String tableNamePattem,String types[])
getTables()方法返回一个ResultSet对象,每一条记录是对一个表的描述。只有那些符合参数要求的表才被返回。
三、获取表中各列的信息
ResultSet getColumns(String catalog,String schemaPattem,String tableNamePattem,String types[])
getColumns()方法返回一个ResultSet类的对象,其中每一行是对一列的描述,只有符合参数要求的列才被返回
四、获取关于索引信息
ResultSet getIndexInfo(String catalog,String schemaPattem,String tableNamePattem,String types[])
getIndexInfo()方法返回一个ResultSet类的对象,其中每一行是对一个索引的描述,只有符合参数要求的索引才被返回。
使用 Statement 执行查询和更新
Statement 对象用于将SQL语句发送到数据库中。实际上有3种 Statement 对象:Statement 、PreparedStatement(它从 Statement 继承而来) 、CallableStatement(它从 PreparedStatement 中继承而来)。
Statement 对象用于执行不带参数的简单SQL语句;
PreparedStatement 对象用于执行 带 或 不带 IN 参数的预编译SQL语句;
CallableStatement 对象用于执行对数据库已存储过程的调用。
也就是说,Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理IN参数的方法。CallableStatement 添加了处理out 参数的方法。
使用ResultSet 处理结果集
(Statement 实例都在前面写了很多例子)
---------------------------------------------------传说中的分割线--------------------------------------------------
使用 PreparedStatement 执行预编译的查询和更新
在前面,我用连接对象 Connection 产生 Statement 对象,然后用 Statement 与数据库处理系统进行交互。Statement 对象在每次执行SQL语句时都将该语句传递给数据库,在多次执行同一语句时,这样的效率就也太低了。
解决这个问题的方法就是 使用 PreparedStatement 对象。如果数据库支持预编译,可以在创建 PreparedStatement 对象时将SQL语句传递给数据库做预编译,以后每次执行这个SQL语句时,速度就可以提高很多。如果不支持预编译,则在语句执行时,才将其传给数据库。这对用户来说是完全透明的。
一般情况下,使用 PreparedStatement 对象都是带输入参数的。下面来看看如何使用PreparedStatement :
1、创建一个 PreparedStatement 对象
PreparedStatement 类是 Statement 类的子类,同Statement类一样,PreparedStatement 类的对象也是建立在Connection 对象之上的。
PreparedStatement pstmt = conn.preparedStatement("update goods set sales=? where goods_name like?");
2、为PreparedStatement 对象提供参数
如果以带输入参数的SQL语句形式创建了一个 PreparedStatement 对象(绝大多数情况下都是这样)。在SQL语句被数据库管理系统正确执行之前,必须为参数(也就是SQL语句中是 ? 的地方)进行初始化。初始化的方法是调用PreparedStatement 类的一系列setXXX() 方法,如果输入的参数类型是int 类型,则调用setInt()方法,如果是String类型,则调用setString()方法。
接上一句例程序:
pstmt.setInt(1,75);
pstmt.setString(2,"googs1");
pstmt.executeUpdate();
注:setXXX() 方法一般有两个参数,第一个参数都是 Int 型,该参数指示 JDBC PreparedStatement 对象的第几个参数将要被初始化,第二个参数值就是PreparedStatement 将要初始化的参数的值,数据类型也就自然相同。
pstmt.setInt(1,75); 该语句使 pstmt对象所应对的SQL语句中参数SALES 的值被初始化为75;
pstmt.setString(2,"googs1");该语句使 pstmt对象所应对的SQL语句中参数GOODS_NAME的值被初始化为goods1。
pstmt.executeUpdate();该语句就是将上面的数据更新。
值得注意的是,当一个对象的参数被初始化之后,该参数的值一直保持不变的,除非它再来一次赋值。
3、设置PreparedStatement 对象的参数
在JSP实际应用中,PreparedStatement 对象要求输入大量的参数值,这就需要使用循环结构来解决繁琐的参数赋值问题,我们继续前面的例子:
int [] salesForWeek = {100,85,95,88,135};
String [] goodsName = {"goods1","goods2","goods3","goods4","goods5"};
int len = goodsName.length;//注意,这儿的length不是length();
for(int i=0;i<len;i++){
pstmt.setInt(1,salesForWeeks[i]);
pstmt.setString(2,goodsName[i]);
pstmt.executeUpdate();
}
代码解释:在这段代码中,用Int型数组来存储将要被更新记录的销售额,用String型数组来存储将要被更新记录的商品名。这两个数组的值一一对应,销售额100对应着商品名为goods1的商品,依次类推。
我们以后想再次更新数据库中的数据时,可以将这段代码当做模板,只需要将数组中的数据进行修改,就可以完成要求的功能了。
---------------------------------------------------传说中的分割线--------------------------------------------------
使用 CallableStatement 执行存储过程。
1、存储过程的概念
存储过程是存储在数据库管理系统服务器上的一组预编译的SQL语句,它是一种封装重复任务操作的方法,支持用户提供的变量,具有强大的编程功能。
2、存储过程的优点
首先,使用存储过程可以与其他应用程序共享应用程序的逻辑,从而确保对数据访问和操作的一致性。
其次,存储过程提供一种安全机制。如果用户被授权执行存储过程的权限,那么,即使该用户没有访问该存储过程所参考表或者视图的权限,也可以执行该存储过程,而不会受到影响。
再者,由于存储过程在第一次执行之后,其执行规划就存储在高速缓冲存储卡中,所以在以后的操作中,只要从高速缓冲中调用已编译好的二进制代码来执行,从而加速了存储过程的执行,提高了系统的功能。
最后,使用存储过程可以减少网络传输所需要的时间,这是使用存储过程的一个非常重要的原因。
3、使用JDBC创建存储过程
存储过程一般是在数据库管理系统中创建的,在应用程序中进行调用,但在有些情况下,应用程序要根据运行环境的情况动态创建存储过程。比如说一种存储过程要向一个表中插入数据,但向哪个表中插入数据是不固定的,要动态决定。这时就要在JSP中创建存储过程,看下面代码:
//创建Statement
Statement stmt = conn.createStatement();
String sqlstr = "create procedure testproc(new_keyword in da
new_author in da
(keyword,title,author,institution,docsource,ndatabase,url) values (new_keyword,new_title,new_author,
new_institution,new_docsource,new_ndatabase,new_url); end;";
stmt.executeUpdate(sqlstr);
注:不同数据库管理系统创建存储过程的SQL语句的语法是有一定的差别的,这个例子用的是Oracle创建存储过程的语法。
4、创建一个CallableStatement 对象
//将前一段代码创建的存储过程名赋值给变量procedurename
String procedurename = ......;
String call_procedure_string = "{call"+procedurename+"(?,?,?,?,?,?,?)}"
//创建一个CallableStatement 对象,该对象和特定的存储过程相关联
CallableStatement cstmt = conn.preparedCall(call_procedure_string);
5、初始化CallableStatement 对象的参数
String keyword = "kword";
String title = "title";
String author = "author";
String institution = "institution";
String docsource = "docsource";
String ndatabase = "ndatabase";
String url = "url";
try{
CallableStatement cstmt = conn.preparedCall(call_procecure_string);
cstmt.setString(1,keyword);
cstmt.setString(2,title);
cstmt.setString(3,author);
cstmt.setString(4,institution);
cstmt.setString(5,docsource);
cstmt.setString(6,ndatabase);
cstmt.setString(7,url);
}catch(Exception e){
}
6、执行CallableStatement 对象对应的executeUpdate()方法
try{
cstmt.executeUpdate();
}catch(SQLException e){
System.out.println("SQLException in insertValue();"+e.getMessage());
}
至此,一个完整的CallableStatement 对象的使用过程便完成了。
对数据库进行并发操作时,为了避免由于并发操作带来的问题,一般要将同一个任务中对数据库的增、删、改、查操作编写到一个事务中,同一个事务中的所有操作要么全部执行成功,要么全部不执行。
因此,JDBC也提供了对事务开发的支持。
-----------------------------------------------------传说中的分割线--------------------------------------------------------
JDBC中,一般在编写事务时应该按照如下步骤进行开发:
1、首选调用setAutoCommit方法禁止自动提交模式,这样多个SQL语句才能处于同一个事务中;
2、然后可以使用Statement 或PreparedStatement 依次执行事务中的SQL语句;
3、当执行完事务中所有的SQL语句后,调用 commit 方法将此事务提交;
4、如果执行过程中出现问题,应该调用 rollback 方法将事务回滚。
OK,下面直接给出一个事务的完整案例:
imp
imp
imp
imp
imp
public class TestJDBC_Transation {
public static void main(String[] args) {
//声明Connection引用
Connection conn = null;
//声明Oracle数据库的连接字符串、用户名和密码
String url = "jdbc:oracle:thin:@localhost:1521:wgy";
String user = "admin";
String password = "123";
try{
//加载Oracle驱动类
Class.forName("oracle.jdbc.driver.OracleDriver");
//连接数据库
conn = DriverManager.getConnection(url,user,password);
//禁用自动提交模式,开始一个事务
conn.setAutoCommit(false);
System.out.println("=================开始事务====================");
//创建Statement对象
Statement stmt = conn.createStatement();
//查询检查music表
System.out.println("=====================打印music表中的记录==========================");
seleceAllFromMusic(stmt);
//向表中插入一行记录
stmt.executeUpdate("insert into music values('001','音乐5','China')");
//再次向表中插入一行记录
stmt.executeUpdate("insert into music values('002','音乐6','UK')");
System.out.println("=======================两条记录插入成功=========================");
//查询检查music表
System.out.println("=====================打印music表中的记录==========================");
seleceAllFromMusic(stmt);
//将事务提交
conn.commit();
//恢复自动提交模式
conn.setAutoCommit(true);
//关闭语句
stmt.close();
}catch(Exception e){
//如果发生错误将发生回滚操作
e.printStackTrace();
try{
conn.rollback();
System.out.println("===============出现问题,事务回滚===================");
}catch(Exception e1){
e1.printStackTrace();
}finally{
try{
conn.close();
}catch(Exception e3){
e3.printStackTrace();
}
}
}
}
//自定义的检索music表全表的方法
public static void seleceAllFromMusic(Statement stmt) throws SQLException{
ResultSet rs = stmt.executeQuery("select * from music");
//打印表头信息
System.out.println("音乐编号\t音乐名称\t所属国家");
//循环打印结果集中的每一条记录
while(rs.next()){
//获得当前记录中字段的内容
String mid = rs.getString(1);
String mname = rs.getString(2);
String mcountry = rs.getString(3);
System.out.println(mid+"\t\t"+mname+"\t\t"+mcountry);
}
//关闭结果集
rs.close();
}
}
-----------------------------------------------------传说中的分割线--------------------------------------------------------
注意:JDBC事务的支持是依赖于所连接的数据库的,如果连接的数据库本身不支持事务,即使正常编写了事务的代码也没有意义,例如,access ,MySQL的免费版 等都不支持事务,所以这个例子是以Oracle数据库为例。
在前面的学习中,总有一个问题,查询所获得的结果集只能使用 next 方法将游标移到下一条记录。确实如此,因为前面的结果集是不可移动的,如果只是想遍历结果集中的数据也就足够了。
但是在某些情况下需要游标能够在结果集中前后移动,这时就需要使用可滚动的结果集了。
-----------------------------------------------------传说中的分割线--------------------------------------------------------
可滚动与不可滚动结果集的比较:
1、之所以在默认的情况下提供不可滚动的结果集,是因为当开发人员从不可滚动的结果集中取出数据后,取出的数据将会从结果集中释放掉,这样就可以大大的节约系统资源,从而提高效率。因此,对于不可滚动结果集,数据只能取出一次,第二次如果再想试图取出同样的数据就会抛异常!
2、而对于可滚动结果集,只要没有被关闭,就要存储所有的数据以备滚动的需要。这在需要哦滚动的情况下提供了方便,但也付出了代价-----需要更多的系统资源。因此,在实际开发中要注意只在必须使用可滚动结果集的情况下使用,没有特殊需求尽量避免使用。
其中的相关方法,查阅API中的 ResultSet 。
-----------------------------------------------------传说中的分割线--------------------------------------------------------
下面,给出一个可滚动结果集的例子:
imp
imp
imp
imp
public class Test_ResultSet_scroll {
public static void main(String[] args) {
//声明Connection引用
Connection conn = null;
try{
//加载JDBC-ODBC桥连接驱动类
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//创建数据库连接
conn = DriverManager.getConnection("jdbc:odbc:stu","","");
//创建能够生成可滚动结果集的Statement
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.TYPE_FORWARD_ON
//执行检索获得可滚动结果集
ResultSet rs = stmt.executeQuery("select * from course");
//打印表头信息
System.out.println("============记录从前至后顺序============");
System.out.println("课程号\t课程名\t学时\t学分");
//按从前至后的顺序打印结果集中的记录
while(rs.next()){
//获取当前记录中各个字段的内容
String cid = rs.getString(1);
String cname = rs.getString(2);
String cperiod = rs.getString(3);
String ccredit_hour = rs.getString(4);
//打印本条记录内容
System.out.println(cid+"\t"+cname+"\t"+cperiod+"\t"+ccredit_hour);
}
//打印表头信息
System.out.println("============记录从后至前顺序============");
System.out.println("课程号\t课程名\t学时\t学分");
while(rs.previous()){
//获取当前记录中各个字段的内容
String cid = rs.getString(1);
String cname = rs.getString(2);
String cperiod = rs.getString(3);
String ccredit_hour = rs.getString(4);
//打印本条记录内容
System.out.println(cid+"\t"+cname+"\t"+cperiod+"\t"+ccredit_hour);
}
//关闭结果集
rs.close();
//关闭语句
stmt.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//关闭数据库连接
conn.close();
}catch(Exception e2){
e2.printStackTrace();
}
}
}
}
批处理是指将一组更新语句一起提送给数据库执行,一般来说这些更新语句都位于同一个事务中。虽然可以将更新语句依次在事务中单独执行,但一起送入数据库效率更高,也更方便,因此在事务中执行批量更新时经常使用批处理。
在Java中编写批处理批处理时将用到Statement 的几个方法。
public void addBacth(String sql) : 将给定的SQL语句添加到此 Statement 对象的当前批处理中;
public void clearBatch() :清空此 Statement 对象当前批处理中的所有SQL语句;
public int [] executeBatch() :执行当前批处理中的所有SQL语句,返回的 int 数组中的元素一次表示批处理中每条SQL语句更新的记录条数。
注意:只应该在批处理中添加对数据库进行更新的SQL语句,不应该添加用于查询的select 语句,因为执行批处理时不会返回结果集。
-------------------------------------------------传说中的分割线----------------------------------------------------
下面给出一个完整的批处理例子:
imp
imp
imp
imp
imp
public class JDBC_Batch_Test {
public static void main(String[] args) {
//声明Connection引用
Connection conn = null;
//声明Oracle数据库的连接字符串,用户名,密码
String url = "jdbc:oracle:thin:@localhost:1521:wgy";
String user = "system";
String password = "123";
try{
//加载Oracle驱动类
Class.forName("oracle.jdbc.driver.OracleDriver");
//创建数据库连接
conn = DriverManager.getConnection(url,user,password);
//禁用自动提交模式,并开始一个事务
conn.setAutoCommit(false);
//创建Statement对象
Statement stmt = conn.createStatement();
//添加批处理中SQL命令
//以后的开发中,其中的SQL语句可以另外写,再传进去,那样就灵活很多,不用写死程序
stmt.addBatch("delete from music");
stmt.addBatch("insert into music values('1002','音乐3','China')");
stmt.addBatch("insert into music values('1003','音乐4','UK')");
//执行批处理
stmt.executeBatch();
//提交事务
conn.commit();
//恢复自动提交模式
conn.setAutoCommit(true);
//执行检索,查看music表
ResultSet rs = stmt.executeQuery("select * from music");
//打印表头信息
System.out.println("音乐编号\t音乐名称\t所属国家 ");
//循环打印结果集中的每一挑记录
while(rs.next()){
//获取当前记录中字段的内容
String mid = rs.getString(1);
String mname = rs.getString(2);
String mcountry = rs.getString(3);
//打印本条记录的内容
System.out.println(mid+"\t\t"+mname+"\t\t"+mcountry);
}
//关闭结果集
rs.close();
//关闭语句
stmt.close();
}catch(Exception e){
//如果出现错误将产生回滚操作
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
finally{
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}