Linux中國

在 Java 中使用外部庫

Java 自帶有一組核心庫,其中包含了定義常用數據類型和相關行為的庫(例如 StringDate)、與主機操作系統交互的實用程序(例如 SystemFile),以及一些用來管理安全性、處理網路通信、創建或解析 XML的有用的子系統。鑒於核心庫的豐富性,程序員通常很容易在其中找到有用的組件,以減少需要編寫的代碼量。

即便如此,核心庫仍有一些功能上的不足,因此發現這些不足的程序員們還額外創建了很多有趣的 Java 庫。例如,Apache Commons「是一個專註於可重用 Java 組件所有方面的 Apache 項目」,提供了大約 43 個開源庫的集合(截至撰寫本文時),涵蓋了 Java 核心庫之外的一系列功能 (例如 geometrystatistics),並增強或替換了 Java 核心庫中的原有功能(例如 mathnumbers)。

另一種常見的 Java 庫類型是系統組件的介面(例如資料庫系統介面),本文會著眼於使用此類介面連接到 PostgreSQL 資料庫,並得到一些有趣的信息。首先,我們來回顧一下庫的重要部分。

什麼是庫?

library 里自然包含的是一些有用的代碼。但為了發揮用處,代碼需要以特定方式進行組織,特定的方式使 Java 程序員可以訪問其中組件來解決手頭問題。

可以說,一個庫最重要的部分是它的應用程序編程介面(API)文檔。這種文檔很多人都熟悉,通常是由 Javadoc 生成的。Javadoc 讀取代碼中的結構化注釋並以 HTML 格式輸出文檔,通常 API 的 package 在頁面左上角的面板中顯示, class 在左下角顯示,同時右側會有庫、包或類級別的詳細文檔(具體取決於在主面板中選擇的內容)。例如,Apache Commons Math 的頂級 API 文檔 如下所示:

![API documentation for Apache Commons Math](/data/attachment/album/202109/10/075802g5nlrmmlxgwcb325.png "API documentation for Apache Commons Math")

單擊主面板中的包會顯示該包中定義的 Java 類和介面。例如,org.apache.commons.math4.analysis.solvers 顯示了諸如 BisectionSolver 這樣的類,該類用於使用二分演算法查找單變數實函數的零點。單擊 BisectionSolver 鏈接會列出 BisectionSolver 類的所有方法。

這類文檔可用作參考文檔,不適合作為學習如何使用庫的教程。比如,如果你知道什麼是單變數實函數並查看包 org.apache.commons.math4.analysis.function,就可以試著使用該包來組合函數定義,然後使用 org.apache.commons.math4.analysis.solvers 包來查找剛剛創建的函數的零點。但如果你不知道,就可能需要更多學習向的文檔,也許甚至是一個實際例子,來讀懂參考文檔。

這種文檔結構還有助於闡明 package (相關 Java 類和介面定義的集合)的含義,並顯示特定庫中捆綁了哪些包。

這種庫的代碼通常是在 .jar 文件 中,它基本上是由 Java 的 jar 命令創建的 .zip 文件,其中還包含一些其他有用的信息。.jar 文件通常被創建為構建過程的端點,該構建過程編譯了所定義包中的所有 .java 文件。

要訪問外部庫提供的功能,有兩個主要步驟:

  1. 確保通過類路徑(或者命令行中的 -cp 參數或者 CLASSPATH 環境變數),庫可用於 Java 編譯步驟(javac)和執行步驟(java)。
  2. 使用恰當的 import 語句訪問程序源代碼中的包和類。

其餘的步驟就與使用 String 等 Java核心類相同,使用庫提供的類和介面定義來編寫代碼。很簡單對吧?不過也沒那麼簡單。首先,你需要了解庫組件的預期使用模式,然後才能編寫代碼。

示例:連接 PostgreSQL 資料庫

在資料庫系統中訪問數據的典型使用步驟是:

  1. 訪問正在使用的特定資料庫軟體代碼。
  2. 連接到資料庫伺服器。
  3. 構建查詢字元串。
  4. 執行查詢字元串。
  5. 針對返回的結果,做需要的處理。
  6. 斷開與資料庫伺服器的連接。

所有這些面向程序員的部分由介麵包 java.sql 提供,它獨立於資料庫,定義了核心客戶端 Java 資料庫連接(JDBC)API。java.sql 包是 Java 核心庫的一部分,因此無需提供 .jar 文件即可編譯。但每個資料庫提供者都會創建自己的 java.sql 介面實現(例如 Connection 介面),並且必須在運行步驟中提供這些實現。

接下來我們使用 PostgreSQL,看看這一過程是如何進行的。

訪問特定資料庫的代碼

以下代碼使用 Java 類載入器Class.forName() 調用)將 PostgreSQL 驅動程序代碼載入到正在執行的虛擬機中:

import java.sql.*;

public class Test1 {

    public static void main(String args[]) {

        // Load the driver (jar file must be on class path) [1]

        try {
            Class.forName("org.postgresql.Driver");
            System.out.println("driver loaded");
        } catch (Exception e1) {
            System.err.println("couldn't find driver");
            System.err.println(e1);
            System.exit(1);
        }

        // If we get here all is OK

        System.out.println("done.");
    }
}

因為類載入器可能失敗,失敗時會拋出異常,所以將對 Class.forName() 的調用放在 try-catch 代碼塊中。

如果你使用 javac 編譯上面的代碼,然後用 java 運行,會報異常:

me@mymachine:~/Test$ javac Test1.java
me@mymachine:~/Test$ java Test1
couldn't find driver
java.lang.ClassNotFoundException: org.postgresql.Driver
me@mymachine:~/Test$

類載入器要求類路徑中有包含 PostgreSQL JDBC 驅動程序實現的 .jar 文件:

me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test1
driver loaded
done.
me@mymachine:~/Test$

連接到資料庫伺服器

以下代碼實現了載入 JDBC 驅動程序和創建到 PostgreSQL 資料庫的連接:

import java.sql.*;

public class Test2 {

        public static void main(String args[]) {

                // Load the driver (jar file must be on class path) [1]

                try {
                        Class.forName("org.postgresql.Driver");
                        System.out.println("driver loaded");
                } catch (Exception e1) {
                        System.err.println("couldn't find driver");
                        System.err.println(e1);
                        System.exit(1);
                }

                // Set up connection properties [2]

                java.util.Properties props = new java.util.Properties();
                props.setProperty("user","me");
                props.setProperty("password","mypassword");
                String database = "jdbc:postgresql://myhost.org:5432/test";

                // Open the connection to the database [3]

                try (Connection conn = DriverManager.getConnection(database, props)) {
                        System.out.println("connection created");
                } catch (Exception e2) {
                        System.err.println("sql operations failed");
                        System.err.println(e2);
                        System.exit(2);
                }
                System.out.println("connection closed");

                // If we get here all is OK

                System.out.println("done.");
        }
}

編譯並運行上述代碼:

me@mymachine:~/Test$ javac Test2.java
me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test2
driver loaded
connection created
connection closed
done.
me@mymachine:~/Test$

關於上述的一些注意事項:

  • 注釋 [2] 後面的代碼使用系統屬性來設置連接參數(在本例中參數為 PostgreSQL 用戶名和密碼)。代碼也可以從 Java 命令行獲取這些參數並將所有參數作為參數包傳遞,同時還有一些其他 Driver.getConnection() 選項可用於單獨傳遞參數。
  • JDBC 需要一個用於定義資料庫的 URL,它在上述代碼中被聲明為 String database 並與連接參數一起傳遞給 Driver.getConnection() 方法。
  • 代碼使用 try-with-resources 語句,它會在 try-catch 塊中的代碼完成後自動關閉連接。Stack Overflow 上對這種方法進行了長期的討論。
  • try-with-resources 語句提供對 Connection 實例的訪問,並可以在其中執行 SQL 語句;所有錯誤都會被同一個 catch 語句捕獲。

用資料庫的連接處理一些有趣的事情

日常工作中,我經常需要知道為給定的資料庫伺服器實例定義了哪些用戶,這裡我使用這個 簡便的 SQL 來獲取所有用戶的列表:

import java.sql.*;

public class Test3 {

        public static void main(String args[]) {

                // Load the driver (jar file must be on class path) [1]

                try {
                        Class.forName("org.postgresql.Driver");
                        System.out.println("driver loaded");
                } catch (Exception e1) {
                        System.err.println("couldn't find driver");
                        System.err.println(e1);
                        System.exit(1);
                }

                // Set up connection properties [2]

                java.util.Properties props = new java.util.Properties();
                props.setProperty("user","me");
                props.setProperty("password","mypassword");
                String database = "jdbc:postgresql://myhost.org:5432/test";

                // Open the connection to the database [3]

                try (Connection conn = DriverManager.getConnection(database, props)) {
                        System.out.println("connection created");

                        // Create the SQL command string [4]

                        String qs = "SELECT " +
                                "       u.usename AS "User name", " +
                                "       u.usesysid AS "User ID", " +
                                "       CASE " +
                                "       WHEN u.usesuper AND u.usecreatedb THEN " +
                                "               CAST('superuser, create database' AS pg_catalog.text) " +
                        "       WHEN u.usesuper THEN " +
                                "               CAST('superuser' AS pg_catalog.text) " +
                                "       WHEN u.usecreatedb THEN " +
                                "               CAST('create database' AS pg_catalog.text) " +
                                "       ELSE " +
                                "               CAST('' AS pg_catalog.text) " +
                                "       END AS "Attributes" " +
                                "FROM pg_catalog.pg_user u " +
                                "ORDER BY 1";

                        // Use the connection to create a statement, execute it,
                        // analyze the results and close the result set [5]

                        Statement stat = conn.createStatement();
                        ResultSet rs = stat.executeQuery(qs);
                        System.out.println("User name;User ID;Attributes");
                        while (rs.next()) {
                                System.out.println(rs.getString("User name") + ";" +
                                                rs.getLong("User ID") + ";" +
                                                rs.getString("Attributes"));
                        }
                        rs.close();
                        stat.close();

                } catch (Exception e2) {
                        System.err.println("connecting failed");
                        System.err.println(e2);
                        System.exit(1);
                }
                System.out.println("connection closed");

                // If we get here all is OK

                System.out.println("done.");
        }
}

在上述代碼中,一旦有了 Connection 實例,它就會定義一個查詢字元串(上面的注釋 [4]),創建一個 Statement 實例並用其來執行查詢字元串,然後將其結果放入一個 ResultSet 實例。程序可以遍歷該 ResultSet 實例來分析返回的結果,並以關閉 ResultSetStatement 實例結束(上面的注釋 [5])。

編譯和執行程序會產生以下輸出:

me@mymachine:~/Test$ javac Test3.java
me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test3
driver loaded
connection created
User name;User ID;Attributes
fwa;16395;superuser
vax;197772;
mbe;290995;
aca;169248;
connection closed
done.
me@mymachine:~/Test$

這是在一個簡單的 Java 應用程序中使用 PostgreSQL JDBC 庫的(非常簡單的)示例。要注意的是,由於 java.sql 庫的設計方式,它不需要在代碼中使用像 import org.postgresql.jdbc.*; 這樣的 Java 導入語句,而是使用 Java 類載入器在運行時引入 PostgreSQL 代碼的方式,也正因此無需在代碼編譯時指定類路徑。

via: https://opensource.com/article/20/2/external-libraries-java

作者:Chris Hermansen 選題:lujun9972 譯者:unigeorge 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國