Java 数据库连接

  Java 的数据库连接编程( JDBC )技术

[ 本讲的知识要点 ] JDBC JDBC 的工作原理, 访问数据库的方法、 Statement PreparedStatement CallableStatement ResultSet 对象的编程使用

9.1 基本知识

9.1.1 JDBC Java DataBase Connectivity Java 数据库连接技术),它是将 Java SQL 结合且独立于特定的数据库系统的应用程序编程接口( API-- 它是一种可用于执行 SQL 语句的 Java API ,即由一组用 Java 语言编写的类与接口所组成)。

     有了 JDBC 从而可以使 Java 程序员用 Java 语言来编写完整的数据库方面的应用程序。另外也可以操作保存在多种不同的数据库管理系统中的数据,而与数据库管理系统中数据存储格式无关。同时 Java 语言的与平台的无关性,不必在不同的系统平台下编写不同的数据库应用程序。

9.1.2 JDBC 设计的目的

1 ODBC :微软的 ODBC 是用 C 编写的,而且只适用于 Windows 平台,无法实现跨平台地操作数据库。

2 SQL 语言: SQL 尽管包含有数据定义、数据操作、数据管理等功能,但它并不是一个完整的编程语言,而且不支持流控制,需要与其它编程语言相配合使用。

3 JDBC 的设计:由于 Java 语言具有健壮性、安全、易使用并自动下载到网络等方面的优点,因此如果采用 Java 语言来连接数据库,将能克服 ODBC 局限于某一系统平台的缺陷;将 SQL 语言与 Java 语言相互结合起来,可以实现连接不同数据库系统,即使用 JDBC 可以很容易地把 SQL 语句传送到任何关系型数据库中。

4 JDBC 设计的目的:它是一种规范,设计出它的最主要的目的是让各个数据库开发商为 Java 程序员提供标准的数据库访问类和接口,使得独立于 DBMS Java 应用程序的开发成为可能(数据库改变,驱动程序跟着改变,但应用程序不变)。

9.1.3 JDBC 的主要功能: 1 )创建与数据库的连接;( 2 )发送 SQL 语句到任何关系型数据库中 ; 3 )处理数据并查询结果。

编程实例:

  try

  { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");  // 1 )创建与数据库的连接

   Connection con=DriverManager.getConnection("jdbc:odbc:DatabaseDSN","Login","Password");

Statement stmt=con.createStatement();

  ResultSet rs=stmt.executeQuery("select * from DBTableName");// 2 )发送 SQL 语句到数据库中                     

  while(rs.next())

  { String name=rs.getString("Name") ;            // 3 )处理数据并查询结果。

     int age=rs.getInt("age");

    float wage=rs.getFloat("wage");

  }

  rs.close();                                                                 // 4 )关闭

  stmt.close();

  con.close();

  }

catch(SQLException e)

{    System.out.println("SQLState:"+ e.getSQLState());

    System.out.println("Message:" + e.getMessage());

    System.out.println("Vendor:"  + e.getErrorCode());

}

9.1.4 JDBC ODBC 的对比,从而体会 JDBC 的特点

1 ODBC 是用 C 语言编写的,不是面向对象的;而 JDBC 是用 Java 编写的,是面向对象的。

2 ODBC 难以学习,因为它把简单的功能与高级功能组合在一起,即便是简单的查询也会带有复杂的任选项;而 JDBC 的设计使得简单的事情用简单的做法来完成。

3 ODBC 是局限于某一系统平台的,而 JDBC 提供 Java 与平台无关的解决方案。

4 )但也可以通过 Java 来操作 ODBC ,这可以采用 JDBc-ODBC 桥接方式来实现(因为 Java 不能直接使用 ODBC ,即在 Java 中使用本地 C 的代码将带来安全缺陷)。

9.1.5 JDBC 驱动程序的类型 : 目前比较常见的 JDBC 驱动程序可分为以下四个种类:

1 JDBC-ODBC 桥加 ODBC 驱动程序

JavaSoft 桥产品利用 ODBC 驱动程序提供 JDBC 访问。注意,必须将 ODBC 二进制代码(许多情况下还包括数据库客户机代码)加载到使用该驱动程序的每个客户机上。因此,这种类型的驱动程序最适合于企业网(这种网络上客户机的安装不是主要问题),或者是用 Java 编写的三层结构的应用程序服务器代码。

JDBC-ODBC 桥接方式利用微软的开放数据库互连接口(ODBC API) 同数据库服务器通讯,客户端计算机首先应该安装并配置ODBC driver JDBC-ODBC bridge 两种驱动程序。

2 )本地 API

这种类型的驱动程序把客户机 API 上的 JDBC 调用转换为 Oracle Sybase Informix DB2 或其它 DBMS 的调用。注意,象桥驱动程序一样,这种类型的驱动程序要求将某些二进制代码加载到每台客户机上。

这种驱动方式将数据库厂商的特殊协议转换成Java 代码及二进制类码,使Java 数据库客户方与数据库服务器方通信。例如:OracleSQLNet 协议,DB2IBM 的数据库协议。数据库厂商的特殊协议也应该被安装在客户机上。

3 JDBC 网络纯 Java 驱动程序

这种驱动程序将 JDBC 转换为与 DBMS 无关的网络协议,之后这种协议又被某个服务器转换为一种 DBMS 协议。这种网络服务器中间件能够将它的纯 Java 客户机连接到多种不同的数据库上。所用的具体协议取决于提供者。通常,这是最为灵活的 JDBC 驱动程序。有可能所有这种解决方案的提供者都提供适合于 Intranet 用的产品。为了使这些产品也支持 Internet 访问,它们必须处理 Web 所提出的安全性、通过防火墙的访问等方面的额外要求。几家提供者正将 JDBC 驱动程序加到他们现有的数据库中间件产品中。

这种方式是纯 Java driver 。数据库客户以标准网络协议 ( HTTP SHTTP) 同数据库访问服务器通信,数据库访问服务器然后翻译标准网络协议成为数据库厂商的专有特殊数据库访问协议 ( 也可能用到 ODBC driver) 与数据库通信。对 Internet Intranet 用户而言这是一个理想的解决方案。 Java driver 被自动的,以透明的方式随 Applets Web 服务器而下载并安装在用户的计算机上。

4 )本地协议纯 Java 驱动程序

这种类型的驱动程序将 JDBC 调用直接转换为 DBMS 所使用的网络协议。这将允许从客户机机器上直接调用 DBMS 服务器,是 Intranet 访问的一个很实用的解决方法。

这种方式也是纯 Java driver 。数据库厂商提供了特殊的 JDBC 协议使 Java 数据库客户与数据库服务器通信。然而,将把代理协议同数据库服务器通信改用数据库厂商的特殊 JDBC driver 。这对 Intranet 应用是高效的,可是数据库厂商的协议可能不被防火墙支持,缺乏防火墙支持在 Internet 应用中会存在潜在的安全隐患。

9.2 JDBC 的工作原理

   JDBC 的设计基于 X/Open SQL CLI (调用级接口) 这一模型。它通过定义出一组 API 对象和方法以用于同数据库进行交互。

 

Java 程序中要操作数据库,一般应该通过如下几步(利用 JDBC 访问数据库的编程步骤):

1 )加载连接数据库的驱动程序   Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

2 )创建与数据源的连接

  String url="jdbc:odbc:DatabaseDSN"; 

  Connection con=DriverManager.getConnection(url,"Login","Password");

3 )查询数据库:创建 Statement 对象并执行 SQL 语句以返回一个 ResultSet 对象。

  Statement stmt=con.createStatement();

  ResultSet rs=stmt.executeQuery("select * from DBTableName");

4 )获得当前记录集中的某一记录的各个字段的值

    String name=rs.getString("Name");

    int age=rs.getInt("age");

    float wage=rs.getFloat("wage");

5 )关闭查询语句及与数据库的连接(注意关闭的顺序先 rs stmt 最后为 con

    rs.close();   

    stmt.close();

    con.close(); 

9.3 JDBC 的结构

   JDBC 主要包含两部分:面向 Java 程序员的 JDBC API 及面向数据库厂商的 JDBC Drive API

1 )面向 Java 程序员的 JDBC API Java 程序员通过调用此 API 从而实现连接数据库、执行 SQL 语句并返回结果集等编程数据库的能力,它主要是由一系列的接口定义所构成。

java.sql.DriveManager :该接口主要定义了用来处理装载驱动程序并且为创建新的数据库连接提供支持。

java.sql.Connection :该接口主要定义了实现对某一种指定数据库连接的功能。

java.sql.Statement :该接口主要定义了在一个给定的连接中作为 SQL 语句执行声明的容器以实现对数据库的操作。它主要包含有如下的两种子类型。

    java.sql.PreparedStatement :该接口主要定义了 用于执行带或不带 IN 参数的预编译 SQL 语句

    java.sql.CallableStatement :该接口主要定义了用于执行数据库的存储过程的雕用。

java.sql.ResultSet :该接口主要定义了用于执行对数据库的操作所返回的结果集。

2 )面向数据库厂商的 JDBC Drive API :数据库厂商必须提供相应的驱动程序并实现 JDBC API 所要求的基本接口(每个数据库系统厂商必须提供对 DriveManager Connection Statement ResultSet 等接口的具体实现),从而最终保证 Java 程序员通过 JDBC 实现对不同的数据库操作。

9.4 数据库应用的模型

1 )两层结构( C/S ):在此模型下,客户端的程序直接与数据库服务器相连接并发送 SQL 语句(但这时就需要在客户端安装被访问的数据库的 JDBC 驱动程序), DBMS 服务器向客户返回相应的结果,客户程序负责对数据的格式化。

client           ODBC/JDBC            Server 端( DBMS

或数据库专用协议     

 

 

主要的缺点: 受数据库厂商的限制,用户更换数据库时需要改写客户程序;受数据库版本的限制,数据库厂商一旦升级数据库,使用该数据库的客户程序需要重新编译和发布;对数据库的操作与处理都是在客户程序中实现,使客户程序在编程与设计时较为复杂。

 

2 )三(或多)层结构( B/S ):在此模型下,主要在客户端的程序与数据库服务器之间增加了一个中间服务器(可以采用 C++ Java 语言来编程实现),隔离客户端的程序与数据库服务器。客户端的程序(可以简单为通用的浏览器)与中间服务器进行通信,然后由中间服务器处理客户端程序的请求并管理与数据库服务器的连接。

 

客户端程序   HTTP RMI CORBA  中间服务器      JDBC    数据库服务器

 


9.5 通过 JDBC 实现对数据库的访问

1 )引用必要的包

  import java.sql.*;  // 它包含有操作数据库的各个类与接口   

2 )加载连接数据库的驱动程序类     

    为实现与特定的数据库相连接, JDBC 必须加载相应的驱动程序类。这通常可以采用 Class.forName() 方法显式地加载一个驱动程序类,由驱动程序负责向 DriverManager 登记注册并在与数据库相连接时, DriverManager 将使用此驱动程序。

      Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

注意:这条语句直接加载了 sun 公司提供的 JDBC-ODBC Bridge 驱动程序类。

3 )创建与数据源的连接

  String url="jdbc:odbc:DatabaseDSN"; 

  Connection con=DriverManager.getConnection(url,"Login","Password");

注意:采用 DriverManager 类中的 getConnection() 方法实现与 url 所指定的数据源建立连接并返回一个 Connection 类的对象,以后对这个数据源的操作都是基于该 Connection 类对象;但对于 Access 等小型数据库,可以不用给出用户名与密码。

  String url="jdbc:odbc:DatabaseDSN"; 

  Connection con=DriverManager.getConnection(url);

  System.out.println(con.getCatalog()); // 取得数据库的完整路径及文件名 

    JDBC 借用了 url 语法来确定全球的数据库(数据库 URL 类似于通用的 URL ),对由 url 所指定的数据源的表示格式为

   jdbc::[ database locator]

jdbc--- 指出要使用 JDBC

subprotocal--- 定义驱动程序类型

database locator--- 提供网络数据库的位置和端口号 ( 包括主机名、端口和数据库系统名等 )     jdbc:odbc://host.domain.com:port/databasefile   

  主协议 jdbc   驱动程序类型为 odbc ,它指明 JDBC 管理器如何访问数据库,该例指名为采用 JDBC-ODBC 桥接方式;其它为数据库的位置表示。   

例如:装载 mySQL JDBC 驱动程序

Class.forName(" org.gjt.mm.mysql.Driver ");

String url

="jdbc:mysql://localhost/softforum?user=soft&password=soft1234&useUnicode=true&characterEncoding=8859_1"
  //testDB 为你的数据库名
  Connection conn= DriverManager.getConnection(url);

例如:装载Oracle JDBC OCI 驱动程序( thin 模式

Class.forName(" oracle.jdbc.driver.OracleDriver ");

String url="jdbc:oracle:thin:@localhost:1521:orcl";
  //orcl 为你的数据库的SID
  String user="scott";
  String password="tiger";
  Connection conn= DriverManager.getConnection(url,user,password);

注意:也可以通过 con.setCatalog("MyDatabase") 来加载数据库。

例如:装载 DB2 驱动程序

Class.forName(" com.ibm.db2.jdbc.app.DB2Driver ")

String url="jdbc:db2://localhost:5000/sample";
  //sample 为你的数据库名
  String user="admin";
  String password="";
  Connection conn= DriverManager.getConnection(url,user,password);

例如:装载 MicroSoft SQLServer 驱动程序

Class.forName(" com.microsoft.jdbc.sqlserver.SQLServerDriver ");

String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=pubs";
  //pubs 为你的数据库的
  String user="sa";
  String password="";   
  Connection conn= DriverManager.getConnection(url,user,password);

4 )查询数据库的一些结构信息

     这主要是获得数据库中的各个表,各个列及数据类型和存储过程等各方面的信息。根据这些信息,从而可以访问一个未知结构的数据库。这主要是通过 DatabaseMetaData 类的对象来实现并调用其中的方法来获得数据库的详细信息(即数据库的基本信息,数据库中的各个表的情况,表中的各个列的信息及索引方面的信息)。

    DatabaseMetaData dbms=con.getMetaData();

    System.out.println(" 数据库的驱动程序为 "+dbms.getDriverName());

5 )查询数据库中的数据:

   JDBC 中查询数据库中的数据的执行方法可以分为三种类型,分别对应 Statement 用于执行不带参数的简单SQL 语句 字符串 ), PreparedStatement (预编译 SQL 语句)和 CallableStatement (主要用于执行存储过程)三个接口。

9.5.1 、实现对数据库的一般查询 Statement

  1 、创建 Statement 对象(要想执行一个 SQL 查询语句,必须首先创建出 Statement 对象,它封装代表要执行的 SQL 语句)并执行 SQL 语句以返回一个 ResultSet 对象,这可以通过 Connection 类中的 createStatement() 方法来实现。

     Statement stmt=con.createStatement();

  2 、执行一个 SQL 查询语句,以查询数据库中的数据。 Statement 接口提供了三种执行SQL 语句的方法:executeQuery()executeUpdate() execute() 。具体使用哪一个方法由SQL 语句本身来决定。

l      方法 executeQuery 用于产生单个结果集的语句,例如 SELECT 语句等。

l      方法 executeUpdate 用于执行INSERTUPDATEDELETE 语句以及SQL DDL (数据定义语言)语句,例如 CREATE TABLE DROP TABLEINSERTUPDATE DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零。

l         方法 execute 用于执行返回多个结果集、多个更新计数或二者组合的语句。一般不会需要该高级功能。

下面给出通过Statement 类中的executeQuery() 方法来实现的代码段。executeQuery() 方法的输入参数是一个标准的SQL 查询语句,其返回值是一个ResultSet 类的对象。

ResultSet rs=stmt. executeQuery ("select * from DBTableName");         
要点:① JDBC 在编译时并不对将要执行的 SQL 查询语句作任何检查,只是将其作为一个 String 类对象,直到驱动程序执行 SQL 查询语句时才知道其是否正确。对于错误的 SQL 查询语句,在执行时将会产生 SQLException

      ②一个 Statement 对象在同一时间只能打开一个结果集,对第二个结果集的打开隐含着对第一个结果集的关闭。

      ③如果想对多个结果集同时操作,必须创建出多个 Statement 对象,在每个 Statement 对象上执行 SQL 查询语句以获得相应的结果集。

      ④如果不需要同时处理多个结果集,则可以在一个 Statement 对象上顺序执行多个 SQL 查询语句,对获得的结果集进行顺序操作。

import java.sql.*;

public class ResultSetTest

{    public static void main(String args[])

        {      try

                {  

                    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

                    Connection con=DriverManager.getConnection("jdbc:odbc:studlist");

                    Statement stmt=con.createStatement();

                    ResultSet rs1=stmt.executeQuery("select name from  student");

                    ResultSet rs2=stmt.executeQuery("select age from student");

// 此时rs1 已经被关闭                   

                    while(rs2.next())

                    {  

                       System.out.println(rs2.getObject(1));

                    }

                    rs2.close();

                    stmt.close();

                    con.close();

                }

                catch(Exception e)

                {

                    System.out.println(e);

                }  

    }

}

注意:

此时显示出的将是姓名还是年龄?(将显示的是rs2 的结果集的内容,即学生的年龄,因为采用JDBC-ODBC 方式的驱动程序时,并且是采用同一个Statement 对象,它只会保留最新的结果集,rs1 中的内容将会被新的结果集所取代)。

  3 关闭 Statement 对象:每一个 Statement 对象在使用完毕后,都应该关闭。

     stmt.close();

9.5.2 、预编译方式执行 SQL 语句 PreparedStatement

    由于 Statement 对象在每次执行 SQL 语句时都将该语句传给数据库,如果需要多次执行同一条 SQL 语句时,这样将导致执行效率特别低,此时可以采用 PreparedStatement 对象来封装 SQL 语句。如果数据库支持预编译,它可以将 SQL 语句传给数据库作预编译,以后每次执行该 SQL 语句时,可以提高访问速度;但如果数据库不支持预编译,将在语句执行时才传给数据库,其效果类同于 Statement 对象。

    另外 PreparedStatement 对象的 SQL 语句还可以接收参数,可以用不同的输入参数来多次执行编译过的语句,较 Statement 灵活方便(详见后文介绍)。

1 创建 PreparedStatement 对象:从一个 Connection 对象上可以创建一个 PreparedStatement 对象,在创建时可以给出预编译的 SQL 语句。

    PreparedStatement pstmt=con.prepareStatement("select * from DBTableName");

2 执行 SQL 语句:可以调用 executeQuery() 来实现,但与 Statement 方式不同的是,它没有参数,因为在创建 PreparedStatement 对象时已经给出了要执行的 SQL 语句,系统并进行了预编译。

    ResultSet rs=pstmt.executeQuery(); // 该条语句可以被多次执行  

3 、关闭 PreparedStatement

    pstmt.close();  // 其实是调用了父类 Statement 类中的 close() 方法

9.5.3 、执行存储过程 CallableStatement

    CallableStatement 类是 PreparedStatement 类的子类,因此可以使用在 PreparedStatement 类及 Statement 类中的方法,主要用于执行存储过程。

1 创建 CallableStatement 对象:使用 Connection 类中的 prepareCall 方法可以创建一个 CallableStatement 对象,其参数是一个 String 对象, 一般格式为:

l         不带输入参数的存储过程“{call 存储过程名()} ”。

l      带输入参数的存储过程“ {call 存储过程名 (?, ?)}

l         带输入参数并 有返回结果参数的 存储过程“ {? = call 存储过程名 (?, ?, ...)}

    CallableStatement cstmt=con.prepareCall("{call Query1()}");

2 执行存储过程:可以调用 executeQuery() 方法来实现。

    ResultSet rs=cstmt.executeQuery();  

3 、关闭 CallableStatement

    cstmt.close();  // 其实是调用了父类 Statement 类中的 close() 方法

6 )检索记录集以获得当前记录集中的某一记录的各个字段的值

9.5.4 ResultSet 对象:

    执行完毕 SQL 语句后,将返回一个 ResultSet 类的对象,它包含所有的查询结果。但对 ResultSet 类的对象方式依赖于光标( Cursor )的类型,而对每一行中的各个列,可以按任何顺序进行处理(当然,如果按从左到右的顺序对各列进行处理可以获得较高的执行效率);

ResultSet 类中的 Course 方式主要有:

ResultSet.TYPE_FORWARD_ONLY (为缺省设置):光标只能前进不能后退,也就是只能从第一个一直移动到最后一个。

ResultSet.TYPE_SCROLL_SENSITIVE :允许光标前进或后退并感应到其它 ResultSet 的光标的移动情形。

ResultSet.TYPE_SCROLL_INSENSITIVE :允许光标前进或后退并不能感应到其它 ResultSet 的光标的移动情形。

ResultSet 类中的数据是否允许修改主要有:

ResultSet.CONCUR_READ_ONLY (为缺省设置):表示数据只能只读,不能更改。

ResultSet.CONCUR_UPDATABLE :表示数据允许被修改。

    可以在创建 Statement PreparedStatement 对象时指定 ResultSet 的这两个特性。

Statement stmt=con.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);

PreparedStatement pstmt=con.PrepareStatement("insert into bookTable values (?,?,?)",ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);

    ResultSet 类的对象维持一个指向当前行的指针,利用 ResultSet 类的 next() 方法可以移动到下一行(在 JDBC 中, Java 程序一次只能看到一行数据),如果 next() 的返回值为 false ,则说明已到记录集的尾部。另外 JDBC 也没有类似 ODBC 的书签功能的方法。

    利用 ResultSet 类的 getXXX() 方法可以获得某一列的结果,其中 XXX 代表 JDBC 中的 Java 数据类型,如 getInt() getString() getDate() 等。访问时需要指定要检索的列(可以采用 int 值作为列号(从 1 开始计数)或指定列(字段)名方式,但字段名不区别字母的大小写)。

while(rs.next())

  {  String name=rs.getString("Name"); // 采用“列名”的方式访问数据

    int age=rs.getInt("age");

    float wage=rs.getFloat("wage");

    String homeAddress=rs.getString(4); // 采用“列号”的方式访问数据

  }

9.5.5 、数据转换

   利用 ResultSet 类的 getXXX() 方法可以实现将 ResultSet 中的 SQL 数据类型转换为它所返回的 Java 数据类型。

9.5.6 NULL 结果值

  要确定给定结果值是否是 JDBC NULL ,必须先读取该列,然后使用 ResultSet.wasNull

方法检查该次读取是否返回 JDBC NULL

  当使用 ResultSet.getXXX 方法读取 JDBC NULL 时,方法 wasNull 将返回下列值之一:

1 Javanull

  对于返回 Java 对象的 getXXX 方法(例如 getString getBigDecimal getBytes getDate getTime getTimestamp getAsciiStream getUnicodeStream getBinaryStream getObject 等)。

2 )零值:对于 getByte getShort getInt getLong getFloat getDouble

3 false 值:对于 getBoolean

9.5.6 、获得结果集中的结构信息: 利用 ResultSet 类的 getMetaData() 方法来获得结果集中的一些结构信息(主要提供用来描述列的数量、列的名称、列的数据类型。利用 ResulSetMetaData 类中的方法)。

ResultsetMetaData  rsmd=rs.getMetaData();

rsmd.getColumnCount();   // 返回结果集中的列数             

rsmd.getColumnLabel(1); // 返回第一列的列名(字段名)

例如:

Statement stmt=con.createStatement();

ResultSet rs=stmt.executeQuery("select * from TableName");

for(int i=1; i<=rs.getMetaData().getColumnCount(); i++)   // 跟踪显示各个列的名称

       {      System.out.print(rs. getColumnName (i)+"/t");

       }

while(rs.next())

{  // 跟踪显示各个列的值

    for(int j=1; j<=rs.getMetaData().getColumnCount(); j++)

       {      System.out.print(rs.getObject(j)+"/t");

       }

}

9.6 、更新数据库

   前面主要介绍如何实现对数据库的查询操作,但在许多应用中需要实现对数据库的更新,这主要涉及修改、插入和删除等(即 SQL 语句中的 Insert Update Delete Creat Drap 等)。仍然通过创建 Statement 对象来实现,但不再调用 executeQuery() 方法,而是使用 executeUpdate() 方法。

要点 F :正确区分 Statement 类中的 executeQuery() execute() executeUpdate() 方法的用法:( 1

executeQuery() 执行一般的 SQL 查询语句(即 SELECT 语句)并返回 Resultset 对象;( 2 execute() 可以执行各种 SQL 查询语句,并可能返回多个结果集(这一般主要发生在执行了返回多个结果集的存储过程时),此时可以采用 Resultset 类的 getResultSet() 来获得当前的结果集;( 3 executeUpdate() 执行对数据库的更新的 SQL 语句或 DDL 语句。

9.6.1 对表中的记录进行操作

   对一个表中的记录可以进行修改、插入和删除等操作,分别对应 SQL Update Insert Delete 操作; executeUpdate() 方法的输入参数仍然为一个 String 对象(即所要执行的 SQL 语句),但输出参数不是 ResultSet 对象,而是一个整数(它代表操作所影响的记录行数)。

Statement stmt=con.createStatement();

stmt.executeUpdate("Update bookTable set Title='Java2' where Author='zhang'");

 

stmt.executeUpdate("Delete from bookTable where Author='zhang'");

stmt.executeUpdate("Insert into bookTable(BookID,Author,Title) values(1,'Li Ming','Java2')");  // 未给出的列,其值为 NULL

 

程序实例: 对数据库中的表进行更新操作并显示操作前后的结果

import java.sql.*;

public class DBUpdateSetTest

{     public static void main(String args[])

         {      try

                {  

                    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

                    Connection con=DriverManager.getConnection("jdbc:odbc:studlist");

                    Statement stmt=con.createStatement();

                    ResultSet rs=stmt.executeQuery("select * from student");

                    System.out.println("Result before executeUpdate");

                    while(rs.next())

                    {

                        System.out.println(rs.getString("name"));

                        System.out.println(rs.getString("age"));

                    }

                    stmt.executeUpdate("Update student set name='Yang' where id=0");

                    stmt.executeUpdate("Delete from student where id=2");

                    stmt.executeUpdate("Insert into student(id,name,age,sex) values(2,'zhang',30,true)");

                    rs=stmt.executeQuery("select * from student");

                    System.out.println("Result After executeUpdate");

                    while(rs.next())

                    {

                        System.out.println(rs.getString("name"));

                        System.out.println(rs.getString("age"));

                    }

                    rs.close();

                    stmt.close();

                    con.close();

                    }

                catch(Exception e)

                {

                    System.out.println(e);

                }  

       }

}

9.6.2  创建和删除表

    创建和删除一个表主要对应于 SQL Create Table Drop Table 语句。这可以通过 Statement 对象的 executeUpdate() 方法来完成。

  创建表

    Statement stmt=con.createStatement();

    stmt.executeUpdate("create table TableName(ID integer, Name VARCHAR(20), Age integer)");

    stmt.executeUpdate("Insert into TableName(ID, Name, Age) values(1,'Yang Ming',30)");

  删除表

    Statement stmt=con.createStatement();

    stmt.executeUpdate("Drop Table TableName");

9.6.3  增加和删除表中的列

  对一个表的列进行更新操作主要是使用 SQL ALTER Table 语句。对列所进行的更新操作会影响到表中的所有的行。

  增加表中的一列

    Statement stmt=con.createStatement();

    stmt.executeUpdate("Alter Table TableName add Column Address VarChar(50)");

    stmt.executeUpdate("Update TableName set Address='Beijing,China' where ID=1");

  删除表中的一列

    Statement stmt=con.createStatement();

    stmt.executeUpdate("Alter Table TableName Drop Column Address");

    stmt.executeQuery("Select * from TableName");

9.6.4  利用 PreparedStatement 对象实现数据更新

    SQL 查询语句一样,对数据更新语句时也可以在 PreparedStatement 对象上执行。使用 PreparedStatement 对象,只需传递一次 SQL 语句,可以多次执行它,并且可以利用数据库的预编译技术,提高执行效率。另外也可以接受参数。

    PreparedStatement pstmt=con.prepareStatement("Update TableName set Address='Beijing,China' where ID >1");

    pstmt.executeUpdate();

9.7 参数的输入与输出

    要实现使用 SQL 语句的输入与输出参数,必须在 PreparedStatement 类的对象上进行操作;同时由于 CallableStatement 类是 PrepareStatement 类的子类,所以在 CallableStatemen 对象上的操作也可以使用输入与输出参数;其主要的编程原理是在生成 CallableStatement PreparedStatement 类的对象时,可以在 SQL 语句中指定输入或输出参数,在执行这个 SQL 语句之前,要对输入参数进行赋值。

1 )使用 PreparedStatement 类的对象

   通过 prepareStatement 类的对象可以实现在查询语句与数据更新语句方面都可以设置输入参数。

    具体的方法是在 SQL 语句中用“?”标明参数,在执行 SQL 语句之前,使用 setXXX 方法给参数赋值,然后使用 executeQuery() executeUpdate() 来执行这个 SQL 语句。每次执行 SQL 语句之前,可以给参数重新赋值。

    setXXX 方法用于给相应的输入参数进行赋值,其中 XXX JDBC 的数据类型,如: Int String 等。 setXXX 方法有两个参数,第一个是要赋值的参数在 SQL 语句中的位置, SQL 语句中的第一个参数的位置为 1 ,第二个参数的位置为 2 setXXX 方法的第二个参数是要传递的值,如 100 、“ Peking ”等,随 XXX 的不同而为不同的类型。

    PreparedStatement pstmt=con.prepareStatement("Update TableName set Name=? where ID=?");

    pstmt.setString(1,"zhang Hua");  // 设置第一个参数( Name )为 zhang Hua

    for(int i=1;i<3;i++)

    {  pstmt.setInt(2,i); // 设置第二个参数( ID )为 1,2

       pstmt.executeUpdate();

    }

要点: 最终实现 Update TableName set Name=zhang Hua where ID=1 Update TableName set Name=zhang Hua where ID=2 的效果。

2 )使用 CallableStatement 对象

    如果要求调用数据库的存储过程,要使用 CallableStatement 对象。另外还有些存储过程要求用户输入参数,这可以在生成 CallableStatement 对象的存储过程调用语句中设置输入参数。在执行这个存储过程之前使用 setXXX 方法给参数赋值,然后再执行这个存储过程。

   CallableStatement cstmt=con.prepareCall("{call Query(?)}");  //Query 为存储过程名

   cstmt.setString(1," 输入参数 ");  // 为存储过程提供输入参数

   ResultSet rs=cstmt.executeQuery();

3 )接收输出参数

    某些存储过程可能会返回输出参数,这时在执行这个存储过程之前,必须使用 CallableStatement registerOutParameter 方法首先登记输出参数,在 registerOutParameter 方法中要给出输出参数的相应位置以及输出参数的 SQL 数据类型。在执行完存储过程以后,必须使用 getXXX 方法来获得输出参数的值。并在 getXXX 方法中要指出获得哪一个输出参数(通过序号来指定)的值。

实例: 存储过程 getTestData 有三个输入参数并返回一个输出参数,类型分别为 VARCHAR 。在执行完毕后,分别使用 getString() 方法来获得相应的值。

CallableStatement cstmt = con.prepareCall(“{? = call getTestData (?,?,?)}”);

cstmt.setString(1,Value);                                 // 设置输入参数

cstmt.setInt(2,Value);

cstmt.setFloat(3,Value);

cstmt.registerOutParameter(1,java.sql.Types.VARCHAR);   // 登记输出参数

ResultSet rs = cstmt.executeQuery();              // 执行存储过程

rs.getString(1);                                                // 获得第一个字段的值

String returnResult=cstmt.getString(1);                      // 获得返回的输出参数的值

要点: 由于 getXXX 方法不对数据类型作任何转换,在 registerOutParameter 方法中指明数据库将返回的 SQL 数据类型,在执行完存储过程以后必须采用相应匹配的 getXXX 方法来获得输出参数的值。

 

9.8 批量处理JDBC 语句提高处理速度

有时候 JDBC 运行得不够快,这可以使用数据库相关的存储过程。当然,作为存储过程的一个替代方案,可以试试使用 Statement 的批量处理特性以提高速度。

  存储过程的最简单的形式就是包含一系列 SQL 语句的过程,将这些语句放在一起便于在同一个地方管理也可以提高速度。 Statement 类可以包含一系列 SQL 语句,因此允许在同一个数据库事务执行所有的那些语句而不是执行对数据库的一系列调用。

  使用批量处理功能涉及下面的两个方法:

   addBatch(String) 方法

   executeBatch 方法

  如果你正在使用 Statement 那么 addBatch 方法可以接受一个通常的 SQL 语句,或者如果你在使用 PreparedStatement ,那么也可以什么都不向它增加。

executeBatch 方法执行那些 SQL 语句并返回一个 int 值的数组,这个数组包含每个语句影响的数据的行数。

注意: 如果将一个 SELECT 语句或者其他返回一个 ResultSet SQL 语句放入批量处理中就会导致一个 SQLException 异常。

  关于 java.sql.Statement 的简单范例可以是:

      con = DriverManager.getConnection(url,"myLogin", "myPassword");

       con.setAutoCommit(false);

       stmt = con.createStatement(); 

       stmt.addBatch("INSERT INTO student " + "VALUES(4,'Yang',20,True)");

       stmt.addBatch("INSERT INTO student " + "VALUES(5,'li',20,True)");

       stmt.addBatch("INSERT INTO student " + "VALUES(6,'zhang',20,True)");

       stmt.addBatch("INSERT INTO student " + "VALUES(7,'wang',20,True)");

       stmt.addBatch("INSERT INTO student " + "VALUES(8,'liu',20,True)");

 

       int [] updateCounts = stmt.executeBatch();

       con.commit();

       con.setAutoCommit(true);

  PreparedStatement 有些不同,它只能处理一部分 SQL 语法,但是可以有很多参数,因此重写上面的范例的一部分就可以得到下面的结果:

  // 注意这里没有删除语句

  PreparedStatement stmt = conn.prepareStatement(

  "INSERT INTO student VALUES(?,?,?,?)"

  );

  User[ ] users = ...;

  for(int i=0; i

  stmt.setInt(1, users[i].getID());

  stmt.setString(2, users[i].getName());

  stmt.setInt(3, users[i].getAge());

  stmt.setBoolean(4, users[i].getSex());

  stmt.addBatch( );

  }

  int[ ] counts = stmt.executeBatch();

如果你不知道你的语句要运行多少次,那么这是一个很好的处理 SQL 代码的方法。在不使用批量处理的情况下,如果添加 50 个用户,那么性能就有影响,如果某个人写了一个脚本添加一万个用户,程序可能变得很糟糕。添加批处理功能就可以帮助提高性能,而且在后面的那种情况下代码的可读性也会更好。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值