使用 JDBC 创建数据库对象 2

    技术2022-05-11  174

    构建更高级别的 JDBC 对象

    从上面的例子可以明显看出,如果可以将我们使用过的一些方法封装在几个更高级别对象中,那将非常有帮助,我们不仅可以封装 try 程序块,而且可以更简单地访问 ResultSet 方法。

    在这一部分中,我们将构建一个新的 resultSet 对象,该对象封装了 JDBC ResultSet 对象,并以 String 数组的形式返回一行数据。我们发现您始终需要从 ResultSetMetaData 对象中获取列的序号和名称,因此,创建一个封装元数据的新对象就非常合理。

    另外,我们经常需要按名称或整数索引提取某行的元素,如果不必总是将这些访问语句包括 try 块中,那将大有帮助。最后一点,如果我们需要整行的内容,则更方便的做法是将整行以String 数组形式返回。在下面所示的 resultSet 对象中,我们致力于实现这些目标:

    class resultSet { //这个类是 JDBC ResultSet 对象的更高级抽象    ResultSet rs;    ResultSetMetaData rsmd;    int numCols;    public resultSet(ResultSet rset)    {       rs = rset;       try       {       //同时获取元数据和列数       rsmd = rs.getMetaData();       numCols = rsmd.getColumnCount();       }       catch (Exception e)       {System.out.println("resultset error"                                 +e.getMessage());}    }    //--    public String[] getMetaData()    {     //返回包含所有列名或其他元数据的     //一个数组       String md[] = new String[numCols];       try          {       for (int i=1; i<= numCols; i++)           md[i-1] = rsmd.getColumnName(i);       }       catch (Exception e)       {System.out.println("meta data error"+                                 e.getMessage());}       return md;    }    //--    public boolean hasMoreElements()    {       try{       return rs.next();       }    catch(Exception e){return false;}    }    //--    public String[] nextElement()    {    //将行的内容复制到字符串数组中       String[] row = new String[numCols];       try       {       for (int i = 1; i <= numCols; i++)              row[i-1] = rs.getString(i);       }       catch (Exception e)       {System.out.println("next element error"+                         e.getMessage());}     return row;    }    //--    public String getColumnValue(String columnName)    {    String res = "";      try       {      res = rs.getString(columnName);      }    catch (Exception e)    {System.out.println("Column value error:"+                 columnName+e.getMessage());}    return res;    }    //--    public String getColumnValue(int i)    {    String res = "";      try       {      res = rs.getString(i);      }    catch (Exception e)    {System.out.println("Column value error:"+                 columnName+e.getMessage());}    return res;    }    //--    public void finalize()    {     try{rs.close();}     catch (Exception e)       {System.out.println(e.getMessage());}    } } 通过简单使用 new 操作符就地创建一个 ResultSet 对象,我们很容易将任何 ResultSet 对象封装在此类中: ResultSet results = ..  //按通常的方法获得ResultsSet //利用它创建一个更有用的对象 resultSet rs = new resultSet(results); 并很容易在任何 JDBC 程序中使用这个对象。

    构建一个 Database 对象

    我们沿 00 链向上移的另一部分努力是创建一个 Database 对象,它将封装下列对象的行为: Connection Statement DatabaseMetaData 对象, 以及我们刚刚构建的 SQL 查询和 resultSet 。我们的 Database 对象允许我们创建连接、获取表名、在数据库中移动以及更简单地获得行和列的值。请注意, Execute 方法返回一个 resultSet 对象,您可以直接对它进行操作。 class Database { //这是一个将 JDBC 数据库的所有功能封装在单个对象中的类 Connection con; resultSet results; ResultSetMetaData rsmd; DatabaseMetaData dma; String catalog; String types[];    public Database(String driver)    {     types = new String[1];     types[0] = "TABLES";         //初始化类型     try{Class.forName(driver);}  //加载 JDBC-ODBC 桥驱动程序     catch (Exception e)     {System.out.println(e.getMessage());}    }    //--    public void Open(String url, String cat)    {     catalog = cat;     try {con = DriverManager.getConnection(url);     dma =con.getMetaData();   //获取元数据     }     catch (Exception e)     {System.out.println(e.getMessage());}    }    //--    public String[] getTableNames()    {     String[] tbnames = null;     Vector tname = new Vector();     //将表名添加到一个 Vector 中,     //因为我们不知道有多少个表     try {       results =                  new resultSet(dma.getTables(catalog, null,                          "%", types));              while (results.hasMoreElements())          tname.addElement(results.getColumnValue("TABLE_NAME"));              }      catch (Exception e) {System.out.println(e);}      //将表名复制到一个 String 数组中       tbnames = new String[tname.size()];       for (int i=0; i< tname.size(); i++)           tbnames[i] = (String)tname.elementAt(i);       return tbnames;    }    //--    public String[] getTableMetaData()    {       // 返回表类型的信息       results = null;       try{       results =                  new resultSet(dma.getTables(catalog, null,                          "%", types));       }       catch (Exception e)       {System.out.println(e.getMessage());}       return results.getMetaData();    }    //--    public String[] getColumnMetaData(String tablename)    {     //返回一个列的数据     results = null;     try {     results =                  new resultSet(dma.getColumns(catalog, null,                          tablename, null));     }     catch (Exception e)    {System.out.println(e.getMessage());}     return results.getMetaData();    }    //--    public String[] getColumnNames(String table)    {    //返回一个列名数组    String[] tbnames = null;    Vector tname = new Vector();    try {    results =                  new resultSet(dma.getColumns(catalog, null,                          table, null));     while (results.hasMoreElements() )        tname.addElement(results.getColumnValue("COLUMN_NAME"));         }    catch (Exception e) {System.out.println(e);}    tbnames = new String[tname.size()];    for (int i=0; i< tname.size(); i++)        tbnames[i] = (String)tname.elementAt(i);    return tbnames;    }  //-- public String getColumnValue(String table,                          String columnName)    {    //返回给定列的值     String res = null;    try       {       if (table.length()>0)           results =                          Execute("Select " + columnName +                                 " from " + table +                                  " order by "+columnName);       if (results.hasMoreElements())          res = results.getColumnValue(columnName);      }    catch (Exception e)    {System.out.println("Column value error" +                 columnName+ e.getMessage());}    return res;    } //--    public String getNextValue(String columnName)    {    // 使用存储的 resultSet    //返回该列的下一个值       String res = "";    try       {       if (results.hasMoreElements())          res = results.getColumnValue(columnName);      }    catch (Exception e)    {System.out.println("next value error"+                 columnName+ e.getMessage());}    return res;  } //--    public resultSet Execute(String sql)    {    //对此数据库执行一个 SQL 查询    results = null;    try       {       Statement stmt = con.createStatement();       results = new resultSet(stmt.executeQuery(sql));     }     catch (Exception e)       {System.out.println("execute error"+                 e.getMessage());}    return results;    } }

    一个可视化的数据库程序

    为了对我们本章学习的内容进行总结,我们编写一个简单的 GUI 程序,它可以显示数据库的表名、列名和列内容。我们还将包括一个文本区域,您可以在其中键入一个要对数据库执行的 SQL 查询。在 Companion CD-ROM 上的 /chapter20 子目录中,可以找到本程序(称为 dbFrame.java )所使用的 resultSet Database 类。程序的显示界面如图 3 所示。

    3:用来显示用 JDBC 连接的数据库中的数据的 dbFrame.java 程序。

    在本程序中,默认数据库 (groceries.mdb) 的表名显示在左侧的栏中。当您单击其中一个表名时,列名就会显示在中间的栏中。最后,当您单击中间栏中的某一行时,该行的内容就会显示在右侧的栏中。

    本程序的关键只是接收列表选择,然后清除并填充正确的列表框:

    public void itemStateChanged(ItemEvent e)    {       Object obj = e.getSource();       if (obj == Tables)     //放入列名          showColumns();       if (obj == Columns)    //放入列的内容          showData();    } //--    private void loadList(List list, String[] s)    {         //清除并填充指定的列表框     list.removeAll();     for (int i=0; i< s.length; i++)          list.add(s[i]);    }    //--    private void showColumns()    {         //显示列名         String cnames[] =                  db.getColumnNames(Tables.getSelectedItem());       loadList(Columns, cnames);    }    //--    private void showData()    {       String colname = Columns.getSelectedItem();       String colval =                  db.getColumnValue(Tables.getSelectedItem(),                          colname);       Data.setVisible(false);       Data.removeAll();       Data.setVisible(true);       colval =                          db.getNextValue(Columns.getSelectedItem());       while (colval.length()>0)           {          Data.add(colval);          colval =                          db.getNextValue(Columns.getSelectedItem());          }    }
    执行查询
    显示画面底部的文本区域使您可键入所需的任何 SQL 查询。演示程序中构建的一个查询如下所示: String queryText = "SELECT DISTINCTROW FoodName, StoreName, Price "+ "FROM (Food INNER JOIN FoodPrice ON "+ "Food.FoodKey = FoodPrice.FoodKey) " + "INNER JOIN Stores ON "+ "FoodPrice.StoreKey = Stores.StoreKey "+ "WHERE (((Food.FoodName)=/'Oranges/')) "+ " ORDER BY FoodPrice.Price;"; 此查询简单地列出每个杂货店的桔子价格。

    当您单击 Run Query 按钮时,它将执行此查询,并将 resultSet 对象传送给一个对话框进行显示:

    public void actionPerformed(ActionEvent e)    {       Object obj = e.getSource();       if (obj == Quit)           System.exit(0);       if (obj == Search)           clickedSearch();    } //--    private void clickedSearch()    {       resultSet rs = db.Execute(query.getText());       String cnames[] = rs.getMetaData();            queryDialog q = new queryDialog(this, rs);       q.show();    }
    查询结果对话框
    查询对话框获得 resultSet 对象,并将每一行放入一个 String 数组中,然后将这些 String 数组放入一个 Vector 中,这样就可以在 paint() 子程序运行期间快速访问这些行。 private void makeTables() { //将每一行放入一个 String 数组中,并将 //这些字符串数组全部放入一个 Vector    tables = new Vector();    String t[] = results.getMetaData();    tables.addElement( t);    while (results.hasMoreElements())                  tables.addElement(results.nextElement());    } 我们通过 Graphics drawString() 方法将数据绘制在一个 Panel 中。就像在 Printer 对象中一样,我们必须自己跟踪 x y 的位置。 public void paint(Graphics g) {  String s[];  int x=0; //计算字体的高度  int y =g.getFontMetrics().getHeight(); //估算列的高度  int deltaX = (int)1.5f*   (g.getFontMetrics().stringWidth("wwwwwwwwwwwwww")); //遍历表矢量  for (int i=0; i< tables.size(); i++)     {    s  = (String[])tables.elementAt(i);         //绘制字符串数组中的每一行    for (int j =0; j< s.length; j++)      {     String st= s[j];     g.drawString(st, x, y);     x += deltaX;           //移到下一列     }    x = 0;                 //开始一个新行    y += g.getFontMetrics().getHeight();         //列标签与列数据之间的额外空间    if (i == 0) y += g.getFontMetrics().getHeight();    } } 内建查询的 queryDialog 如图 4 所示。

    4dbFrame 程序中 显示的 queryDialog,其中显示的是默认查询的结果。

    示例文件

    groceries.zip dbFrame.zip jdbc-odbc Bridge

    小结

    在本文中,我们讨论了数据库以及检验数据库并对数据库执行查询的方法。我们已经看到, JDBC 提供了一种与平台和数据库无关的、面向对象的方法来访问这些数据,我们还学习了 JDBC 的主要对象: ResultSet ResultSetMetaData DatabaseMetaData在用这些对象编写了一个简单的程序之后,我们设计了更高级别的 resultSet Database 对象,我们用它们构建了一个简单的可视化界面来显示数据库信息。

    如果您熟悉数据库的强大功能,就会认识到 SQL 语言可使您执行比我们此处所述操作更强大的任务。例如,您可以创建新表、添加、更改或删除表的行、列或单个表元。使用 JDBC,所有这一切都变得通用和易于处理。

    如果您使用的是特定平台的数据库驱动程序,如 JDBC-ODBC Bridge,则您在编写应用程序时会受到限制,因为 applet 不能连接在另一台计算机上运行的这个桥。其他客户机-服务器数据库,如 IBM DB2,允许您使用 applet 中的 JDBC 与其连接。


    最新回复(0)