`

Apache Derby 进行Java数据库开发:第 2 部分

阅读更多

执行 JDBC 查询:概述

 

JDBC API 是按照层次结构方式设计的,其中一种类型的对象包含其他类型的对象。例如,通过 JDBC Connection 建立数据库连接;并且为了向数据库发送 SQL 查询,从相应的 Connection 对象创建一个 JDBC Statement 对象。虽然这看上去合乎逻辑,但是存在一个重要影响:如果由于某种原因关闭了数据库连接,那么也将关闭该数据库 包含 的所有数据库对象。此层次结构扩展了多个层次,因为使用了 ResultSet 对象(包含在相应的 Statement 对象内)访问查询的结果。图 1 演示了此层次结构。


图 1. JDBC 对象之间的容器关系
JDBC 对象之间的容器关系

虽然在本文中您不会这样做,但是如果要重新执行 JDBC 查询,则将重用所有底层 ResultSet 对象。这意味着您必须先处理完查询结果,然后再重用 JDBC Statement —— 例如,重新发出数据库查询 —— 否则将丢失上一次查询到的所有结果。

要对 Apache Derby 数据库执行查询,首先需要有一个已经正确初始化的数据库。如果您已经阅读了 本系列教程 的前几篇文章,则可能已经具有一个可用于本文其余内容的数据库。如果未阅读过或者想要重新开始,可以使用清单 1 中所示的 derby.build.sql 脚本文件。


清单 1. 初始化 Apache Derby 工作区

                rb$ mkdir derbyWork
rb$ cd derbyWork/
rb$ unzip ../derby10.zip Archive:  ../derby10.zip
  inflating: derby.build.sql         
  inflating: FirstQuery.java         
  inflating: SecondQuery.java        
  inflating: ThirdQuery.java         
rb$ java org.apache.derby.tools.ij < derby.build.sql > derby.build.out 2> derby.build.err
rb$ javac *.java
rb$ ls
FirstQuery.class        ThirdQuery.class        derby.build.sql
FirstQuery.java         ThirdQuery.java         derby.log
SecondQuery.class       derby.build.err         test
SecondQuery.java        derby.build.out

 

 

如 清单 1 所示,应当先创建一个新的工作目录,然后解压缩本文附带的源代码文件(请参阅本文末尾的 下载 部分)。下一步是创建一个正确初始化的 Apache Derby 数据库,通过从 Apache Derby ij 工具运行 SQL 脚本即可轻松完成这一步。最后,编译这三个内含的 Java 源代码文件。虽然不是显式显示结果(因为某些结果会生成冗长的输出),但是可以执行每一个这样的 Java 应用程序,例如,在命令提示符处输入 java FirstQuery 。本文的其余部分分别提供了这三个示例的源代码。

 



 

执行查询

如上一部分所述,执行数据库查询包括三个主要概念:

 

  • 数据库连接
  • 查询语句
  • 查询结果

要使用这些对象,必须先将其导入到应用程序中,如清单 2 所示。


清单 2. 启动 JDBC 查询应用程序

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.ResultSet;

public class FirstQuery {
    private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
    private static final String url = "jdbc:derby:test" ;
    private static final String qry = 
        "SELECT itemNumber, price, stockDate, description FROM bigdog.products" ;	
 

 

显式查询:最佳实践
通常,在编写数据库查询时应当总是显式的。避免使用未命名列的查询(即使用 SELECT * 子句,该子句可以在应用程序与底层数据库之间创建一个松散耦合)。如果对数据库作了任何更改,例如重命名列、更改列的数据类型或者添加/删除列,则应用程 序仍将工作但可能生成错误结果。通过显式列出各个列,可以将出现此类错误的可能性减至最低,因为代码将按预期运行,除非受请求的列被修改。在这种情况下, 代码将生成一个错误消息,显式地通知您出现的问题。

清单 2 中的代码片段首先显式导入所有必要的 Java 类,然后定义将在第一个数据库查询程序的其余部分中使用的几个重要常量。这个过程的大部分步骤应当看上去很熟悉;您在 本系列教程的上一篇文章 中看到过大部分步骤。新的部分是显式包括用于 StatementResultSet JDBC 接口的 import 语句,以及稍后在程序中执行的查询字符串。此查询显式列出 bigdog.products 表中的四列,这是一个最佳实践(请参阅侧栏 显式查询)。这样做可以消除在底层数据库被意外修改的情况下可能出现的大量潜在错误。

在 Java 程序中执行数据库查询所需的操作相对简单,如清单 3 所示,其中给出了 FirstQuery.java 程序的 doQuery 方法。注意,此方法的签名包括 throws SQLException 子句,该子句表示在此方法内生成的任何 SQL 异常必须由调用代码来处理(您将在 清单 4 中看到如何正确完成此操作)。


清单 3. 执行数据库查询

static void doQuery(Connection con) throws SQLException {
        
    SQLWarning swarn = null ;
            
    Statement stmt = con.createStatement() ;
    ResultSet rs = stmt.executeQuery(qry) ;
        
    while (rs.next()) {
            
        System.out.println("Item Number: " + rs.getString("itemNumber")) ;
        System.out.println("Item Price:  " + rs.getString("price")) ;
        System.out.println("Stock Date:  " + rs.getString("stockDate")) ;
        System.out.println("Description: " + rs.getString("description") + '\n') ;
            
        swarn = rs.getWarnings() ;
            
        if(swarn != null){
            printSQLWarning(swarn) ;
        }
    }
    rs.close() ;
    stmt.close() ;
}
 

doQuery 方法中,首先使用 Connection 对象的 createStatement 方法创建一个新的 JDBC Statement 对象。使用 Statement 对象的 executeQuery 方法将查询字符串发送给执行查询字符串的 Apache Derby 数据库。通过使用 Apache Derby 内嵌的 JDBC 驱动程序包提供的 ResultSet 实现可以访问 Java 程序中的查询结果。

ResultSet 接口提供了大量技术,可用于访问查询返回的数据。在 doQuery 方法中,使用迭代技术,藉此使用 next 方法来循环访问正在执行的查询所得到的每一行数据。最初,这个迭代器被放在 第一行之前 ,所以当它第一次被调用后,迭代器将指向第一行。 每执行一次 while 循环,将访问结果数据集中的下一行直到到达最后一行。在处理了最后一行之后,迭代器将变为 null ,因为您已经移到 最后一行之外 ;这将导致循环结束。

在循环内,通过使用 ResultSet 对象的 getString 方法访问每行的四列;此方法将列值作为 Java String 对象进行检索,这样您可以直接打印行值。getString 方法可以使用两种方法访问列:在查询中使用列的序号或使用列的名称。例如,上一个查询中,getString(1) 等价于 getString("itemNumber") 。通常,显式查询是一种最佳实践并且通常为首选,因为它将减少应用程序中出现微小错误的机会。此外,将从第 1 列开始访问,而不是第 0 列,这可能是问题的另一个根源 —— 这进一步促使了显式查询的使用。

虽然未必会生成任何警告,但是 doQuery 方法将在您访问每个新行后明确地检查关于 ResultSet 对象的新警告。这是必要的,因为每次访问一个新行都会清除以前所有关于 ResultSet 的警告。在此方法中不这样做,但是还可以以同一种方法检查关于 Statement 对象的 SQL 警告。

要使用 doQuery 方法,需要建立数据库连接并且从 try ... catch 块内调用方法,如清单 4 所示。


清单 4. 调用 doQuery 方法

public static void main(String[] args) {
    Connection con = null ;

    try {
        Class.forName(driver) ;
        con = DriverManager.getConnection(url);

        SQLWarning swarn = con.getWarnings() ;

        if(swarn != null){
            printSQLWarning(swarn) ;
        }

        doQuery(con) ;

    } catch (SQLException se) {
        printSQLException(se) ;
    } 
...
 

如您所见,通过将查询执行代码封装到单独的方法中,只需要对 先前文章 中提供的数据库连接代码的 main 方法做一处修改。建立了数据库连接并且检查了可能的 SQL 警告后,调用 doQuery 方法,该方法将处理所有查询处理的细节。在本文的其余部分中,将修改 doQuery 方法中的逻辑以了解关于从 Java 程序中对 Derby 数据库执行 select 查询的更多信息。

 



 

提取合适类型的数据

在上一部分中,您了解了如何发出查询以及如何将数据提取为字符串,如果仅仅是打印数据的话这已经足够了。但是大多数时候,需要将业务逻辑应用到数据中,这要求将数据提取为合适的数据类型。例如,可能需要将 price 提取为数字货币数据类型并且将 stockDate 提取为日期数据类型。

幸运的是,JDBC API 提供了将数据提取为适当的数据类型的方法。在形式上,用于类型匹配的规则十分冗长,但是因为 Apache Derby 数据库是用 Java 语言编写的,所以类型匹配就更加简单了。完整的语法规则可在 Apache Derby 参考手册中获得,本文的 参考资料 部分中列出了该手册。但是,通常类型匹配就是您期望的内容,如清单 5 所示,其中显示了 SecondQuery.java 程序的 doQuery 方法。


清单 5. 类型匹配

 static void doQuery(Connection con) throws SQLException {
    int itemNumber = -1 ;
    BigDecimal price = null ;
    Date stockDate = null ;
    String description = null ;
    
    BigDecimal threshold = new BigDecimal(40.00) ;
    
    SQLWarning swarn = null ;
    Statement stmt = con.createStatement() ;
    ResultSet rs = stmt.executeQuery(qry) ;
    
    while (rs.next()) {
        
        itemNumber = rs.getInt("itemNumber") ;
        price = rs.getBigDecimal("price") ;
        stockDate = rs.getDate("stockDate") ;
        description = rs.getString("description") ;
        
        swarn = rs.getWarnings() ;
        
        if(swarn != null){
            printSQLWarning(swarn) ;
        }else{
            if((itemNumber < 6)&&(price.compareTo(threshold) > 0)){
                System.out.println("Item Number: " + itemNumber) ;
                System.out.println("Item Price:  " + price) ;
                System.out.println("Stock Date:  " + stockDate) ;
                System.out.println("Description: " + description + '\n') ;
            }
        }
    }
    rs.close() ;
    stmt.close() ;
}
 

 

这段示例代码显示了修改后的 doQuery 方法,该方法现在可以将查询结果提取为适当的数据类型。price 列被提取为 java.math.BigDecimal 数据类型,stockDate 列被提取为 java.sql.Date 数据类型。虽然没有显示在此代码清单(而是文件 SecondQuery.java 中的完整源代码清单的一部分)中,但是需要包括相应的 import 语句,以使您可以在程序中使用这两个类。

doQuery 方法的这个新实现中,迭代查询结果集中的各个行,但是现在每列被提取为相应的数据类型。结果将像以前一样打印,但是这一次只打印 itemNumber 列值小于 6 并且其 price 小于某个阈值(在本例中为 40.00 美元)的那些行。ResultSet 可以是具有多个重要方法的复杂对象。如下一部分所示,ResultSet 有自己的元数据,您可以使用 ResultSetMetaData 对象访问这些元数据。

 



 

使用查询元数据

即使只选择了几行,前面两个 Java 应用程序也生成了冗长的查询结果清单。在本系列教程的较早几篇文章中,使用了 Apache Derby ij 工具来执行数据库操作(在本文开始时还使用了它来执行内含的 SQL 脚本)。ij 工具可以生成具有良好格式的查询输出,使用 ResultSetMetaData 对象和格式化打印语句也可以实现同样的功能,如清单 6 所示,显示了 ThirdQuery.java 程序的 doQuery 方法。


清单 6. 执行格式化的查询

     static void doQuery(Connection con) throws SQLException {
    int itemNumber = -1 ;
    BigDecimal price = null ;
    Date stockDate = null ;
    String description = null ;

    int numRows = 0 ;
    String line = "------------------------------------" ;
    BigDecimal threshold = new BigDecimal(40.00) ;
 
    SQLWarning swarn = null ;
    Statement stmt = con.createStatement() ;
    ResultSet rs = stmt.executeQuery(qry) ;
    ResultSetMetaData rsmd = rs.getMetaData() ;
        
    System.out.printf("%-11s|", rsmd.getColumnName(1)) ;
    System.out.printf("%-8s|", rsmd.getColumnName(2)) ;
    System.out.printf("%-10s|", rsmd.getColumnName(3)) ;
    System.out.printf("%-40s\n", rsmd.getColumnName(4)) ;
    
    System.out.println(line + line);

    while (rs.next()) {
        
        itemNumber = rs.getInt("itemNumber") ;
        price = rs.getBigDecimal("price") ;
        stockDate = rs.getDate("stockDate") ;
        description = rs.getString("description") ;
        
        swarn = rs.getWarnings() ;
        
        if(swarn != null){
            printSQLWarning(swarn) ;
        }else{
            numRows ++ ;
            System.out.printf("%-11s|", itemNumber) ;
            System.out.printf("%-8s|", price) ;
            System.out.printf("%-10s|", stockDate) ;
            System.out.printf("%-40s\n", description) ;
        }
    }
    
    System.out.println("\n" + numRows + " rows selected") ;
    
    rs.close() ;
    stmt.close() ;
}
 

本例将使用 ResultSetMetaData 对象提取并使用给定的 ResultSet 对象的元数据。虽然它不显示在这段代码清单中,但是需要导入 ResultSetMetaData 接口,该接口显示在附带的 ThirdQuery.java 源代码文件中。此元数据包含大量信息,使您可以以一种对数据库更加通用的方法来编写 Java 应用程序。对于利用 Apache Derby 数据库的 Java 应用程序来说,这种通用性通常不是必需的,因为 Java 应用程序与嵌入式 Apache Derby 数据库(它本身就是 Java 应用程序)之间通常存在着紧密耦合。但是,如果需要处理某些随机的 SQL 命令,由于是由 Derby ij 工具完成的,因此对此元数据进行访问是至关重要的。

本例中的另一项主要创新是使用了格式化打印。您可能以前没有看到过这种打印方式,格式化 print 语句将把一个格式字符串应用到后续数据中以创建特定格式的输出。在这种情况下,需要限定所显示的每列的长度并插入竖线 (|) 来分隔列,以模拟 ij 工具的输出。例如,"%-11s|" 表示打印长度限定为 11 个字符的字符串,后接一个竖线。减号表示使字符串左对齐。

分享到:
评论

相关推荐

    apache derby 学习资料

    收集的学习资料,包括:Derby数据库(V10.9)用户手册(PDF版),用 Apache Derby 进行 Java 数据库开发,用 Apache Derby 进行数据库开发,03开源项目(三)嵌入式数据库Apache Derby(1)(开发指南).pdf ,Apache Derby ...

    嵌入式数据库Apache+Derby开发指南

    嵌入式数据库Apache+Derby开发指南

    嵌入式数据库Apache Derby(入门指南)

    嵌入式数据库Apache Derby是用 Java 语言编写的,所以可以在任何存在合适的 Java 虚拟机(JVM)的地方运行,Derby软件绑定在Java档案(JAR)文件中,只有2MB大小.

    derby数据库免安装jvm内置数据库

    Apache Derby非常小巧,核心部分derby.jar只有2M,所以既可以做为单独的数据库服务器使用,也可以内嵌在应用程序中使用。Cognos 8 BI的Content Store默认就是使用的Derby数据库,可以在Cognos8的安装目录下看到一个...

    Apache Derby(入门指南jar)

    嵌入式数据库Apache Derby是用 Java 语言编写的,所以可以在任何存在合适的 Java 虚拟机(JVM)的地方运行,Derby软件绑定在Java档案(JAR)文件中,只有2MB大小.

    apache Derby jar包

    apachederby纯java数据库的jar包

    Apache Derby 10.4版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    Apache Derby 10.10版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    Apache Derby 10.5版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    spring-boot-derby:Apache Derby嵌入式数据库的Spring Boot Java技术员

    春天引导德比Apache Derby嵌入式数据库的Spring Boot Java技术员

    springboot+derby+mybatisplus+swagger2例子

    Apache Derby 是100% Java 编写的内存数据库,属于 Apache 的一个开源项目。并且是一个容易管理的关系数据库管理系统 Apache Derby 是一个与平台无关的数据库引擎,它以 Java 类库的形式对外提供服务。与其他难以...

    derby 数据库开发文档

    Apache Derby 是一种高质量的、纯 Java™ 的嵌入式关系数据库引擎,IBM® 最近已将其捐献给开放源码社区。Derby 数据库基于文件系统,具有高度的可移植性,并且是轻量级的,这使得它非常便于发布。

    db-derby-10.14.2.0-lib.zip

    Derby数据库是一个纯用Java实现的珍袖型的数据库,属于Apache的一个开源项目。由于是用Java实现的,所以可以在任何平台上运行;另外一个特点是体积小,免安装,java1.6开始集成了derby数据库,位于jdk下面的db目录下...

    Apache Derby 10.8版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    derby数据库

    详细描述了derby的使用,Derby数据库是一个纯用Java实现的内存数据库,属于Apache的一个开源项目。由于是用Java实现的,所以可以在任何平台上运行;另外一个特点是体积小,免安装,只需要几个小jar包就可以运行了。

    Apache Derby 10.7版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    Apache Derby 10.9版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    Apache Derby 10.6版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    Apache Derby 10.1版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

    Apache Derby 10.3版手册集

    Apache Derby是Apache软件基金会所研发的开放源码数据库管理系统;由于Derby是一个纯Java程式,因此只需要操作系统支援Java虚拟机,Derby便可执行。

Global site tag (gtag.js) - Google Analytics