Java 8 的發布帶來了期待已久的函數式編程支援,其中最令人興奮的應用領域莫過於資料庫操作。傳統的 JDBC 程式碼冗長、易出錯且缺乏型別安全,而 Java 8 的 Stream API 配合像 Jinq 這樣的創新庫,讓資料庫查詢變得如同操作記憶體中的集合一樣簡單直觀。
以往篩選比利時客戶需要編寫冗長的迴圈:
Collection<Customer> belgians = new ArrayList<>();
for (Customer c : customers) {
if (c.getCountry().equals("Belgium")) {
belgians.add(c);
}
}
Java 8 Stream API 將其簡化為單行:
customers.stream()
.filter(c -> c.getCountry().equals("Belgium"));
更強大的是,只需將 stream() 改為 parallelStream() 即可啟用並行處理,無需重寫任何邏輯:
customers.parallelStream()
.filter(c -> c.getCountry().equals("Belgium"));
Jinq 是一個開源專案,探索如何將函數式編程帶入資料庫查詢。其語法與 Stream API 几乎相同:
// 記憶體集合查詢
customers.stream().filter(c -> c.getCountry().equals("Belgium"));
// 資料庫查詢(Jinq)
customers.where(c -> c.getCountry().equals("Belgium"));
關鍵差異在於:Jinq 會在執行時將 Lambda 表達式翻譯為 SQL 查詢並由資料庫引擎執行,而非在記憶體中載入所有資料過濾。
Jinq 如何將 Java 位元碼轉換為 SQL?核心技術是符號執行:
.class 檔案,獲取位元碼c.getCountry().equals("Belgium"))例如這段位元碼:
d = c.getCountry()
e = "Belgium"
e = d.equals(e)
return e
經符號執行分析後,Jinq 推導出查詢條件:WHERE country = 'Belgium'。
Jinq 支援 SQL-92 標準的複雜查詢:選擇、投影、連接、子查詢等。且因使用標準 Java 語法,編譯器可在編譯期發現型別錯誤:
customers
.where(c -> c.getCountry().equals("Belgium"))
.where(c -> {
if (c.getSalary() < 100000)
return c.getSalary() < c.getDebt();
else
return c.getSalary() < 2 * c.getDebt();
});
此查詢會被翻譯為帶有 CASE WHEN 的複雜 SQL,且全程享有 Java 的型別檢查。
PreparedStatement、ResultSet 繁瑣操作隨著 Java 8 普及,期待更多主流資料庫驅動(如 Hibernate、MyBatis)原生支援函數式查詢 API,讓開發者徹底擺脫字串拼接 SQL 的時代。