JDBC 操作数据库


第 12 章 操作数据库 教学目标: 本章讲述的是 Java 的 JDBC 技术。JDBC 是 Java 访问数据库的程序接口, 通过 JDBC,我们就能够以 SQL 语言对数据库进行操作。本章首先简单介绍了 SQL 语言,接着讲述的是 JDBC 的基本操作,包括连接数据库、结果集的获取 和处理、错误处理等,最后本章还介绍了 JDBC 的一些高级操作,如使用 PreparedStatement、事务控制和存储过程。 教学重点与难点: ◆ JDBC 技术规范 ◆ SQL 语言 ◆ Statement 和 PreparedStatement 的使用 ◆ 结果集的处理 ‹ 数据库事务控制 ‹ 存储过程 12.1 JDBC 技术 JDBC 是一种用于执行 SQL 语句的 Java API。它由一组用 Java 语言编写成的类和接口 组成。JDBC 为编程人员提供了一个标准的 API,使得他们能够用纯 Java API 来编写数据 库应用程序。 12.1.1 JDBC 技术简介 JDBC 有以下的特点: JDBC 是 SQL 水平上的 API。JDBC 是为 Java 语言定义的一个 SQL 调用级界面,其中 心在于执行基本的 SQL 声明的取回结果。在此基础上可以定义更高层次的 API。 与 SQL 的一致性。结构化查询语言 (SQL) 是访问关系数据库的标准语言。困难之处 在于:虽然大多数的 DBMS (数据库管理系统)对其基本功能都使用了标准形式的 SQL, 但它们却不符合最近为更高级的功能定义的标准 SQL 语法或语义。例如,并非所有的数 据库都支持储存程序或外部连接,那些支持这一功能的数据库又相互不一致。人们希望 SQL 中真正标准的那部份能够进行扩展以包括越来越多的功能。但同时 JDBC API 又必 须支持现有的 SQL。 JDBC API 解决这个问题的一种方法是允许将任何查询字符串一直传到所涉及的 DBMS 驱动程序上。这意味着应用程序可以使用任意多的 SQL 功能,但它必须冒这样的 风险:有可能在某些 DBMS 上出错。事实上,应用程序查询甚至不一定要是 SQL,或者 说它可以是个为特定的 DBMS 设计的 SQL 的专用派生物(例如,文档或图象查询)。 JDBC 处理 SQL 一致性问题的第二种方法是提供 ODBC 风格的转义子句。 转义语法为几个常见的 SQL 分歧提供了一种标准的 JDBC 语法。例如,对日期文字 和已储存过程的调用都有转义语法。 对于复杂的应用程序,JDBC 用第三种方法来处理 SQL 的一致性问题。它利用 DatabaseMetaData 接口来提供关于 DBMS 的描述性信息,从而使应用程序能适应每个 DBMS 的要求和功能。 由于 JDBC API 将用作开发高级数据库访问工具和 API 的基础 API,因此它还必须 注意其所有上层建筑的一致性。“符合 JDBC 标准 TM”代表用户可依赖的 JDBC 功能的 标准级别。要使用这一说明,驱动程序至少必须支持 ANSI SQL-2 Entry Level(ANSI SQL-2 代表美国国家标准局 1992 年所采用的标准。Entry Level 代表 SQL 功能的特定清单)。 驱动程序开发人员可用 JDBC API 所带的测试工具包来确定他们的驱动程序是否符合这 些标准。 12.1.2 JDBC 技术规范 现在我们来看看 JDBC 规范。JDBC 规范历史上经历了几次重大的版本更新: JDBC 1.0:提供基本的功能,强调易用性。 JDBC 2.0:提供更多高级功能以及服务器端的处理能力。 JDBC 3.0:完善了 API,优化性能。改进了连接池、语句缓冲机制,提供了向 Sun 连 接器体系的迁移途径。 一些在 JDBC 2.0 规范中可选的功能,例如分布式事务,在 JDBC 3.0 规范中是必需的。 同时,JDBC 3.0 还定义了一些新的特性,例如在缓冲池中缓冲经过预处理的命令等。 最初的 Java 语言规范并没有规定 Java 程序如何访问数据库。但不久之后,Sun 和它的 合作者就开始填补这个空白。早期的 Java 数据访问策略依赖于建立通向 ODBC(ODBC 是 Microsoft 发起的数据源访问标准)的桥梁,结果就是 JDBC-ODBC 桥接驱动程序。今天, JDBC 驱动程序总共有四种类型: DBC-ODBC 桥,再加上 ODBC 驱动程序;本机 API, 部分是 Java 的驱动程序;面向数据库中间件的纯 Java 驱动程序;直接面向数据库的纯 Java 驱动程序。 第三、四两类都是纯 Java 的驱动程序,因此,对于 Java 开发者来说,它们在性能、 可移植性、功能等方面都有优势。 第一类 JDBC 驱动程序是 JDBC-ODBC 桥再加上一个 ODBC 驱动程序。Sun 建议第一 类驱动程序只用于原型开发,而不要用于正式的运行环境。桥接驱动程序由 Sun 提供,它 的目标是支持传统的数据库系统。Sun 为该软件提供关键问题的补丁,但不为该软件的最 终用户提供支持。一般地,桥接驱动程序用于已经在 ODBC 技术上投资的情形,例如已经 投资了 Windows 应用服务器。 尽管 Sun 提供了 JDBC-ODBC 桥接驱动程序,但由于 ODBC 会在客户端装载二进制代 码和数据库客户端代码,这种技术不适用于高事务性的环境。另外,第一类 JDBC 驱动程 序不支持完整的 Java 命令集,而是局限于 ODBC 驱动程序的功能。 第二类 JDBC 驱动程序是本机 API 的部分 Java 代码的驱动程序,用于把 JDBC 调用转 换成主流数据库 API 的本机调用。这类驱动程序也存在与第一类驱动程序一样的性能问题, 即客户端载入二进制代码的问题,而且它们被绑定了特定的平台。 第二类驱动程序要求编写面向特定平台的代码,这对于任何 Java 开发者来说恐怕都不 属于真正乐意做的事情。主流的数据库厂商,例如 Oracle 和 IBM,都为它们的企业数据库 平台提供了第二类驱动程序,使用这些驱动程序的开发者必须及时跟进不同数据库厂商针 对不同操作系统发行的各个驱动程序版本。 另外,由于第二类驱动程序没有使用纯 Java 的 API,把 Java 应用连接到数据源时,往 往必须执行一些额外的配置工作。很多时候,第二类驱动程序不能在体系结构上与大型主 机的数据源兼容;即使做到了兼容,效果也是差强人意。 由于诸如此类的原因,大多数 Java 数据库开发者选择第三类驱动程序,或者选择更灵 活的第四类纯 Java 新式驱动程序。 第三类 JDBC 驱动程序是面向数据库中间件的纯 Java 驱动程序,JDBC 调用被转换成 一种中间件厂商的协议,中间件再把这些调用转换到数据库 API。第三类 JDBC 驱动程序 的优点是它以服务器为基础,也就是不再需要客户端的本机代码,这使第三类驱动程序要 比第一、二两类快。另外,开发者还可以利用单一的驱动程序连接到多种数据库。 第四类 JDBC 驱动程序是直接面向数据库的纯 Java 驱动程序,即所谓的“瘦”(thin) 驱动程序,它把 JDBC 调用转换成某种直接可被 DBMS 使用的网络协议,这样,客户机和 应用服务器可以直接调用 DBMS 服务器。对于第四类驱动程序,不同 DBMS 的驱动程序 不同。因此,在一个异构计算环境中,驱动程序的数量可能会比较多。但是,由于第四类 驱动程序具有较高的性能,能够直接访问 DBMS,所以这一问题就不那么突出了。 12.2 SQL 语言 SQL 是结构化查询语言(Structured Query Language)的简称。 SQL 语言除了具有查询数据库的功能意外,还有定义数据结构、修改数据和说明安全 性约束条件等特性。 SQL 现在已经成为关系数据库的标准语言,它最早是由 IBM 的 San Jose 研究室提出的, 是 70 年代初作为项目 System R 的一部分而实施的。 美国国家标准协会(ANSI)和国际标准化组织(ISO)制定了一系列的 SQL 标准。 12.2.1 删除和修改表 一旦已经建立了一个表,你就不能删除表中的字段或者改变字段的数据类型。在这种 情况你所能做的是删除这个表,然后重头开始。 要删除一个表,你可以使用 SQL 语句 DROP TABLE。例如,又从数据库中彻底删除 表 mytable,你要使用如下的语句: DROP TABLE mytable 使用 DROP TABLE 命令时一定要小心。一旦一个表被删除之后,你将无法恢复它。 当你建设一个站点时,你很可能需要向数据库中输入测试数据。而当你准备向世界提 供你的网点时,你会想清空表中的这些测试信息。如果你想清除表中的所有数据但不删除 这个表,你可以使用 TRUNCATE TABLE 语句。例如,下面的这个 SQL 语句从表 mytable 中删除所有数据: TRUNCATE TABLE mytable 虽然你不能删除和修改已经存在的字段,但你可以增加新字段。最容易的实现方法是 使用 SQL 事务管理器中的 Manager Tables 窗口。你也可以使用 SQL 语句 ALTER TABLE。 下面是一个如何使用这种语句的例子: ALTER TABLE mytable ADD mynewcolumn INT NULL 这个语句向表 mytable 中增加了一个新字段 mynewcolumn。当你增加新字段时,你必 须允许它接受空值,因为表中原来可能已经有了许多记录。 12.2.2 SELECT 语句 SQL 的 SELECT 语句用于从表中读取数据,SELECT 语句一般由 SELECT 字句、FROM 字句和 WHERE 字句构成,其中 WHERE 子句可以省略。 SELECT 后面的是数据库表的域。 语句: SELECT * FROM user_table; 将返回表 user_table 的所有内容,如果只想得到所用用户的 Email 地址,那么应该这 样写: SELECT Email FROM user_table; 下面的语句将返回所有用户的 ID 和名字: SELECT ID,Name FROM user_table; 这里的 Email、ID 和 Name 都是表 user_table 的域。 FROM 子句后面跟的是表格的名字,由于用 SELECT 语句查询出来的结果也是一个关 系,即可以看作一个新的表格。所以 FROM 后面可以是 SELECT 出来的结果。 例如语句: SELECT ID FROM (SELECT ID,Name FROM user_table); 这个语句首先从 user_table 中读取出用户的 ID 和用户的名字,然后再从这个结果中读 取出用户的 ID。 WHERE 字句用来指定查询的条件。 例如语句: SELECT Email FROM user_table WHERE ID="John"; 这个语句读取的是用户 ID 为"John"的用户的 Email 地址。 WHERE 字句中的逻辑运算符可以使用 AND、OR 和 NOT,例如: SELECT Email FROM user_table WHERE ID="John" OR ID="Jack"; 此例中的查询条件比前一个复杂了一点。这个语句从表 user_table 中选出所有 ID 域为 "John"或"Jack"的记录。如果表中含有"John"或"Jack"的多个 Email 地址,所有的 Email 地址 都被读取。 SELECT 语句的结构看起来很直观。如果你请一个朋友从一个表中为你选择一组记录, 你也许以非常相似的方式提出你的要求。在 SQL SELECT 语句中,你"SELECT 特定的列 FROM 一个表 WHERE 某些列满足一个特定的条件"。 12.2.3 插入、删除和修改记录 INSERT 用于,向表中添加一个新记录,如: INSERT mytable (mycolumn) VALUES ('some data') 这个语句把字符串'some data'插入表 mytable 的 mycolumn 字段中。将要被插入数据的 字段的名字在第一个括号中指定,实际的数据在第二个括号中给出。 INSERT 语句的完整句法如下: INSERT [INTO] {table_name|view_name} [(column_list)] {DEFAULT VALUES |Values_list | select_statement} 如果一个表有多个字段,通过把字段名和字段值用逗号隔开,你可以向所有的字段中 插入数据。假设表 mytable 有三个字段 first_column,second_column,和 third_column。下面 的 INSERT 语句添加了一条三个字段都有值的完整记录: INSERT mytable (first_column,second_column,third_column) VALUES ('some data','some more data','yet more data') ELETE 用于从表中删除一个或多个记录。你可以给 DELETE 语句提供 WHERE 子句。 WHERE 子句用来选择要删除的记录。例如,下面的这个 DELETE 语句只删除字段 first_column 的值等于"Delete Me"的记录: DELETE mytable WHERE first_column='Deltet Me' DELETE 语句的完整句法如下: DELETE [FROM] {table_name|view_name} [WHERE clause] 在 SQL SELECT 语句中可以使用的任何条件都可以在 DELECT 语句的 WHERE 子句 中使用。例如,下面的这个 DELETE 语句只删除那些 first_column 字段的值为'goodbye'或 second_column 字段的值为'so long'的记录: DELETE mytable WHERE first_column='goodby' OR second_column='so long' 如果你不给 DELETE 语句提供 WHERE 子句,表中的所有记录都将被删除。 UPDATE 用于修改表中已经存在的一条或多条记录。同 DELETE 语句一样,UPDATE 语句可以使用 WHERE 子句来选择更新特定的记录。请看这个例子: UPDATE mytable SET first_column='Updated!' WHERE second_column='Update Me!' 这个 UPDATE 语句更新所有 second_column 字段的值为'Update Me!'的记录。对所有 被选中的记录,字段 first_column 的值被置为'Updated!'。 下面是 UPDATE 语句的完整句法: UPDATE {table_name|view_name} SET [{table_name|view_name}] {column_list|variable_list|variable_and_column_list} [,{column_list2|variable_list2|variable_and_column_list2}… [,{column_listN|variable_listN|variable_and_column_listN}]] [WHERE clause] 12.2.4 聚类函数 假如想对一个表中的记录进行数据统计,这时候需要使用聚类函数。 函数 COUNT()也许是最有用的聚类函数。你可以用这个函数来统计一个表中有多少条 记录。例如,想了解 Jack 发表了多少篇文章,那么相应的 SQL 语句是: SELECT COUNT(*) AS Num FROM article_table where ID='Jack' 这句语句统计 ID 为"Jack"的记录的数量,并把这个值放在字段名为 Num 的字段中。 函数 AVG()返回一个字段中所有值的平均值。 函数 SUM()返回一个字段中所有值的和。 函数 MAX()返回一个字段的所有值中的最小值。如果字段是空的,函数 MAX()返回空 值。 函数 MIN()返回一个字段的所有值中的最小值。如果字段是空的,函数 MIN()返回空 值。 12.3 JDBC 基本操作 本小节介绍 JDBC 的基本操作,一般来说这些操作对以 JDBC 应用来说是必须的。一 个 JDBC 程序一开始必须加载 JDBC 驱动,然后连接到数据库并利用 Statement 接口执行操 作或查询,查询操作会返回结果集或元数据集,程序对这些数据进行处理,如果发生异常, 程序还需要处理异常。 12.3.1 加载 JDBC 驱动 为了和特定的数据源建立连接,JDBC 必须加载相应的驱动程序。驱动程序可以是前 面所介绍的四种驱动程序中的一种。 我们可以用 Class.forName 方法显式的加载一个驱动程序,根据驱动程序的不同,主要 有两种加载方法: 加载 JDBC-ODBC 桥驱动程序的方法为: Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); 这个语句直接加载了 sun 公司提供的 JDBC-ODBC 桥驱动程序。 或者加载其他 JDBC 驱动程序: Class.forName("driver-class-name"); 参数 driver-class-name 根据不同的数据源,不同的驱动程序有不同的值。例如: 装载 MySQL 驱动程序 mm: Class.forName("org.gjt.mm.mysql.Driver"); 装在 MSSQL 驱动程序 JSQLConnect: Class.forName("com.jnetdirect.jsql.JSQLDriverr"); 12.3.2 连接数据库 DriverManager 类的 getConnection 方法用于建立与某个数据源的连接。语法一般如下 Connection Conn=DriverManager.getConnection(url,user,password); 参数说明: url:url 对象指定所要连接数据源。根据驱动程序的不同,url 的写法也有不同,但一 般都遵守一下格式中的一种: jdbc:driver-id:database-id jdbc:driver-id//host/database-id 用户在使用的时候可以参加驱动程序的说明文档以获取正确的写法。 user,password:用户和密码。 例如,用 JSQLConnect 建立和 MSSQL 的连接: String sConnStr ="jdbc:JSQLConnect://127.0.0.1/PUBS"; Connection Conn=DriverManager.getConnection(sConnStr,"sa",""); 12.3.3 Statement 接口 在连接上数据库后,下一步就是对数据库进行具体的操作,如查询、修改等。这一过 程要用到 Statement 接口。总的来说,有三类 Statement 接口:Statement、PreparedStatement 和 CallableStatement。 Statement 对象用于一般查询语句的执行。在执行一个 SQL 查询语句前,必须首先创 建一个 Statement 对象。利用 Connection 类的 createStatement 方法可以创建一个 Statement 对象。 例如: Statement stmt=Conn.createStatement(); Statement 对象的 executeQuery 方法用于执行一个查询语句。executeQuery 方法的参数 是一个 String 对象,即一个 SQL 查询语句。它的返回值是一个 ResultSet 类的对象。 例如: ResultSet rs=stmt.executeQuery("SELECT * FROM user_table"); 在 Statement 对象使用完毕以后,应该关闭它: stmt.close(); Statement 对象在每次执行 SQL 语句时都将该语句传给数据库。在多次执行同一语句 的时候,这样做的效率会很低。这时可以使用 PreparedStatement 对象。它可以将 SQL 语句 传给数据库作与编译,以后每次执行这个 SQL 的时候,速度具可以提高很多。 PreparedStatement 是 Statement 的一个子接口,因此它可以使用 Statement 接口中的方 法。 在使用 PreparedStatement 时,首先要创建对象。创建对象的时候应当给出要预编译的 SQL 语句,例如: PreparedStatement pstmt=Conn.prepareStatement("SELECT * FROM user_table"); 然后执行语句: ResultSet rs=pstmt.executeQuery(); 最后要关闭 PreparedStatement: pstmt.close(); CallableStatement 对象用于执行数据库重的存储过程。存储过程即数据库中已经存在 的 SQL 查询语句。执行改存储过程的结果同执行相应的 SQL 语句时一样的。 CallableSatement 类是由 PreparedStatement 派生的子接口。 同样,在使用 CallabelStatement 对象的时,首先要创建对象。它的参数是一个 String 对象,一般的格式是"{call procedurename()}",其中 procedurename 是存储过程的名称。 例如,如果要执行数据库中的 Query1: CallableStatement cstmt=Conn.prepareCall("{call Query1()}"); 然后执行存储过程: ResultSet rs=cstmt.execuiteQuery(); 最后关闭 CallableStatement cstmt.close(); 12.3.4 结果集 执行 SQL 查询语句以后,这些语句执行的结果都返回一个 ResultSet 类的对象。要把 查询的结果在显示出来,就必须对 ResultSet 对象进行一定的处理,本小节讲述对结果集的 一般处理方法。 SQL 语句发送后,返回的结果通常存放在一个 ResultSet 类的对象中,ResultSet 可以 看作是一个表,这个表包含由 SQL 返回的列名和相应的值,ResultSet 对象中维持了一个指 向当前行的指针。最初,这个指针指向表的第一行之前。Result 类的 next 方法可以使指针 向下移动一行,因此第一次使用 next 方法将指针移到的一行,这时候可以对的一行进行处 理。处理完毕之后再用 next 使得指针移向第二行,继续处理第二行数据。Result 类的 next 方法返回一个 boolean 类型的值,如果这个值是 true,那么说明已经成功的移动到了下一行, 如果是 false 那么说明表已经没有下一行了,也就是说,整个表已经处理完毕。 注意,在使用 next 对 ResultSet 对象操作之前,应当判断一下 ResultSet 的对象值是否 为 NULL,即空集。 方法 getXXX 提供了获取当前行中某列值的途径。这里的 XXX 指的是 JDBC 中 java 的数据类型,例如 getInt,getString 等等。在每一行内,可按任何次序获取列值。但为了保 证可移植性,应该从左至右获取列值,并且一次性地读取列值。 列名或列号可用于标识要从中获取数据的列。例如,如果 ResultSet 对象 rs 的第二 列名为"title",并将值存储为字符串,则下列任一代码将获取存储在该列中的值: String s = rs.getString("title"); String s = rs.getString(2); 注意列是从左至右编号的,并且从列 1 开始。同时,用作 getXXX 方法的输入的列 名不区分大小写。下面是简单检索的一段程序: Statement stmt = conn.creatStetement(); String SQL="select ID,Name from user_table"; ResultSet rs=stmt.executeQuery(SQL); while(rs.next()){ //移动指针到下一行 String id=rs.getID(1); //得到第一列,即 ID 的值 String name=rs.getString("Name"); //得到 Name 的值 System.out.println(id+" "+name); //输出结果 } 12.3.5 元数据集 为了发现数据库的信息,必须获取 DatabaseMetaData 对象。一旦程序已经获取有效 的 Connection,下面代码可以获取元数据对象: DatabaseMetaData dbmd = con.getMetaData(); 通过 DatabaseMetaData 可以获取的信息包括数据库名称、驱动程序名称、版本、可用 的最大连接数等等。但需要注意,不是所有的 DBMS 都支持 DatabaseMetaData 所有的方 法。 12.3.6 错误处理 java.sql 包中的许多方法都会抛出 SQLException,它和任何其他例外一样要求捕捉语 句块。SQLException 的目的是描述数据库或驱动程序错误(例如,SQL 语法)。除了从 Throwable 继承下来的标准 getMessage(),SQLException 还有两种提供详细资料的方法: 一个方法是 getNextException(),另一种方法是 setNextException(SQLException ex)。 getSQLState()返回基于 X/ Open SQL 规范说明的 SQLState 标识符。这些会在 DBMS 手册中列出。 getErrorCode()用来检索特定于数据库开发商的错误代码。 getNextException()得到下一个 SQLException,在没有其他 SQLException 时为 null。 程序和数据库间很多的情况都可能出错。本方法允许跟踪全部发生的问题。 setNextException() 允许程序员添加 SQLException。 这些方法应该相当直接。典型的捕捉代码看起来如下所示: try { // 数据库操作 } catch ( SQLException SQLe) { while( SQLe != null) { //错误处理 SQLe = SQLe.getNextException(); } } SQLWarning 是 SQLException 的子类,但成因和其他的例外不一样。程序员必须明 确地索取警告。Connection、Statement 和 ResultSet 都拥有允许检索的 getWarnings() 方法。 还有避免复制检索的 clearWarnings() 方法。SQLWarning 类本身仅仅添 加了方法 getNextWarning() 和 setNextWarning()。 SQLWarning 非常类似于传统的编译程序警告:发生不完全正确的某些事情,但严重 结果又不足以结束处理。 Statement 在进行下一个执行时自动地清除警告。ResultSet 每次访问新行时都清除警 告。API 文档对于 Connection 而言是静态的。为谨慎起见,请在警告给出后发布 clearWarnings()。 获取 SQLWarning 的典型代码如下: try { ... stmt = con.createStatement(); sqlw = con.getWarnings(); while( sqlw != null) { // 处理 SQLWarnings sqlw = sqlw.getNextWarning(); } con.clearWarnings(); stmt.executeUpdate( sUpdate ); sqlw = stmt.getWarnings(); while( sqlw != null) { // 处理 SQLWarnings sqlw = sqlw.getNextWarning(); } } catch ( SQLException SQLe) { ... } 12.4 JDBC 高级操作 除了上面所介绍的基本操作外,JDBC 还提供了另外一些相对比较高级的功能。如 PreparedStement 接口用于预先编译或筹备 SQL 语句、事务控制和存储过程等。本小节将对 这些技术一一加以讨论。 12.4.1 PreparedStatement PreparedStatement 是 Statement 的子接口,PreparedSatement 有以下好处: 已包含的 SQL 会被发送到数据库,并预先进行编译或筹备。如果该 SQL 语句已经编 译或筹备,则本步骤会被省略。根据 DBMS 的不同,SQL 可能会被进行高速缓存并重复 使用。即使对不同的 PreparedStatement 也是如此,并且大部分工作由 DB MS 而不是驱动 程序完成。 PreparedStatement 可以处理列值的 IN 参数, 该参数的作用非常类似于方法的参数。 例如,PreparedStatement 能处理直接运行很易于出错的数据变换,这些数据变换是迅 速地在 SQL 上创建的;并以一种对开发者来说透明的方式处理引用和日期。 这里,有两个创建 PreparedStatement 的范例: pstmtU = con.prepareStatement( "UPDATE myTable SET myStringColumn = ? " + "WHERE myIntColumn = ?" ); pstmtQ = con.prepareStatement( "SELECT myStringColumn FROM myTable " + "WHERE myIntColumn = ? "); 其中的“?”也被称作参数标志,是在语句执行前待设置的值。我们可以从 1 开始按 从左到右的数字顺序对它们进行引用。PreparedStatement 使用 setXXX()方法来设置这些值, 下面是在上述语句中设置值的范例: pstmtU.setString( 1, "myString" ); pstmtU.setInt( 2, 1024 ); pstmtU.executeUpdate(); pstmtQ.setInt( 1, 1024 ); pstmtQ.executeQuery(); PreparedStatement 是从 Statement 继承下来的,因此包括了 Statement 所有的功能。 一般而言,重复运行同一查询或者需要多次更改某一列的值时,请考虑使用筹备语句。 下面是运用 PreparedStatement 的一个简单的例子: import java.sql.*; public class pst { public static void main(String args[]) { String url = "jdbc:odbc:Coffee"; Connection con; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); } try { con = DriverManager.getConnection(url, "sa", "jovial"); PreparedStatement updateSales; String updateString = "update COFFEES " + "set SALES = ? where COF_NAME like ?"; updateSales = con.prepareStatement(updateString); updateSales.setInt(1,88); updateSales.setString(2,"Colombian_Decaf"); int n = updateSales.executeUpdate(); System.out.println(n); updateSales.close(); con.close(); } catch(SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } } } 12.4.2 使用事务控制 在 SQL 术语中,事务是逻辑工作单元(logical unit of work,LUW)构成的一个或多 个语句。事务用来表示或全或无的系列操作;也就是说,要么一切十分成功,要么什么也 没有发生。事务控制用于控制一系列操作的原子性。 典型的事务是从银行帐户提款,并存放到另一个。完成这个操作我们必须先从帐户提 款,然后存入另一个帐户。显而易见提款和存款必须是原子性的,否则如果钱成功提出来, 但是存入失败,那么提出来的钱就凭空消失了,这显然是不允许的。 虽然一些 SQL 非标准语言有专门的开始和结束事务语句,但总的来说事务会从程序 开始持续进行到 Statement 提交(commit)。并从该点,开始新的事务。这是 JDBC 所使用 的模型。JDBC 驱动程序的默认值是 autocommit,表示每个 SQL 语句的 结果一旦执行 就永久保留。 在 autocommit 模式中,commit 出现于 Statement 完成时。Statement 返回 ResultSet 时, 直到最后一行已经检索或 ResultSet 关闭,Statement 才完成。 setAutoCommit 方法是处理事务的关键。采用 Connection.setAutoCommit(false)进行事 务控制。该方法可被随时调用,必要时,可在程序多次调用。调用 Connection.setAutoCommit(false) 后,Connection.commit()和 Connection.rollback() 就用来 控制 LUW。一旦 autocommit 设置为 false,全部数据库语句在 commit 前都可以视为暂时 性的。 下面是使用事务控制的一个例子: con.setAutoCommit(false); PreparedStatement updateSales = con.prepareStatement( "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?"); updateSales.setInt(1, 50); updateSales.setString(2, "Colombian"); updateSales.executeUpdate(); PreparedStatement updateTotal = con.prepareStatement( "UPDATE COFFEES SET TOTAL = TOTAL + ? WHERE COF_NAME LIKE ?"); updateTotal.setInt(1, 50); updateTotal.setString(2, "Colombian"); updateTotal.executeUpdate(); con.commit(); con.setAutoCommit(true); Connection.rollback() 用来进行回滚操作。在发生异常或程序检测出错误状态或数据错 误时,应该使用这个方法。 12.4.3 存储过程 将常用的或很复杂的工作,预先用 SQL 语句写好并用一个指定的名称存储起来, 那么 以后要叫数据库提供与已定义好的存储过程的功能相同的服务时,只要调用这个存储过程 就可以了。 存储过程有以下的优点: 存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。 当对数据库进行复杂操作时(如对多个表进行 Update、Insert、Query、Delete 时),可 将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。 存储过程可以重复使用,可减少数据库开发人员的工作量 安全性高,可设定只有某此用户才具有对指定存储过程的使用权 必须说明,并被所有的 DBMS 都支持存储过程,而不同的 DBMS 的存储过程语法也 不尽相同。在一些 DBMS 中,下面的语句创建了一个存储过程: create procedure SHOW_SUPPLIERS as select SUPPLIERS.SUP_NAME, COFFEES.COF_NAME from SUPPLIERS, COFFEES where SUPPLIERS.SUP_ID = COFFEES.SUP_ID order by SUP_NAME 上面这些语句可以在 Java 程序中放入一个字符串 createProcedure 中,并且创建一个 Statement 对象,调用 executeUpdate()方法执行这些 SQL 语句。这样就在数据库系统中创 建了一个叫做 SHOW_SUPPLIERS 的存储过程。 String createProcedure = "create procedure SHOW_SUPPLIERS " + "as " + "select SUPPLIERS.SUP_NAME, COFFEES.COF_NAME " + "from SUPPLIERS, COFFEES " + "where SUPPLIERS.SUP_ID = COFFEES.SUP_ID " + "order by SUP_NAME"; Statement stmt = con.createStatement(); stmt.executeUpdate(createProcedure); 创建了存储过程后,我们以后就可以随时调用它。如: CallableStatement cs = con.prepareCall("{call SHOW_SUPPLIERS}"); ResultSet rs = cs.executeQuery(); 执行的结果将返回一个结果集,以后的操作就和一般的 Statement 一样了。 这个例子完整的代码如下: //创建存储过程 import java.sql.*; public class pro { public static void main(String args[]) { String url = "jdbc:odbc:Coffee"; Connection con; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); } try { con = DriverManager.getConnection(url, "sa", "jovial"); String createProcedure = "create procedure SHOW_SUPPLIERS " + "as " + "select SUPPLIERS.SUP_NAME, COFFEES.COF_NAME " + "from SUPPLIERS, COFFEES " + "where SUPPLIERS.SUP_ID = COFFEES.SUP_ID " + "order by SUP_NAME" + " EXECUTE SHOW_SUPPLIERS" + " GO"; Statement stmt = con.createStatement(); stmt.executeUpdate(createProcedure); con.close(); stmt.close(); } catch(SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } } } //调用存储过程 import java.sql.*; public class prosql { public static void main(String args[]) { String url = "jdbc:odbc:Coffee"; Connection con; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); } try { con = DriverManager.getConnection(url, "sa", "jovial"); CallableStatement cs = con.prepareCall("{call SHOW_SUPPLIERS}"); ResultSet rs = cs.executeQuery(); while (rs.next()) { String s = rs.getString(1); String f = rs.getString(2); System.out.println(s + " " + f); } con.close(); } catch(SQLException ex) { System.err.println("SQLException: " + ex.getMessage()); } } } 12.5 本章小结 JDBC 是一种用于执行 SQL 语句的 Java API。它由一组用 Java 语言编写成的类和接口 组成。JDBC 为编程人员提供了一个标准的 API,使得他们能够用纯 Java API 来编写数据 库应用程序。 SQL 是结构化查询语言的简称,它有查询数据库、数据结构、修改数据和说明安全性 约束条件等功能。SQL 现在已经成为关系数据库的标准语言。 使用 JDBC,首先必须加载 JDBC 驱动,然后使用 getConnection()方法连接数据库,最 后调用 Statement 接口的方法用执行 SQL 操作或返回结果集,并对结果集进行处理。 Statement 接口可以分类三种:Statement、PreparedStatement 和 CallableStatement。 Statement 对象用于一般查询语句的执行。 PreparedStatement 对象。它可以将 SQL 语句传给数据库作与编译,以后每次执行这个 SQL 的时候,速度具可以提高很多。 CallableStatement 对象用于执行数据库重的存储过程。存储过程即数据库中已经存在 的 SQL 查询语句。 12.6 上机练习 1. 安装一个数据库系统,可供选择的数据库如 MySQL、Microsoft SQL Server 等。 2. 利用数据库系统提供的界面练习 SQL 语言。使用 SQL 语言进行如下操作: (1) 创建表和删除表; (2) 查询记录; (3) 插入、删除和修改记录; (4) SQL 语言的聚类函数。 3. 验证本章的例子。 4. 编写 Java 语言实现如下操作: (1) 创建表和删除表; (2) 查询记录; (3) 插入、删除和修改记录。 可以根据需要使用 Statement、PreparedStatement 和存储过程。 12.7 习题 1. 什么是 JDBC 技术?什么是 SQL 语言? 2. 比较 Statement、PreparedStatement 和 CallableStatement 的异同。并说明如何决定应该 使用哪一种 Statement。 3. 什么是事务控制?试举例说明之。Java 中如何使用事务控制?
还剩16页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

amina

贡献于2014-07-11

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf