愉快地學Java語言:第十七章 訪問數據庫

導讀

本文適合Java入門,不太適合Java中高級軟件工程師。本文以《Java核心技術》第10版為藍本,採用不斷提出問題,然後解答問題的方式來講述。本篇文章只是這個系列中的一篇,如果你喜歡這種講解方式,或者覺得從中能學到知識,可以關注我,以便查閱本系列其他文章。

愉快地學Java語言:第十七章 訪問數據庫

讓我們開始愉快地學習Java語言吧!

1認識JDBC

什麼是JDBC?

JDBC是一個術語,而不是Java DataBase Connectivity的首字母縮寫。它提供了訪問數據庫的API,並且可以使用結構化查詢語言(SQL)完成對數據庫的查找和更新。

JDBC的成功與其設計思想是分不開的,那麼其設計思想是什麼呢?

JDBC與ODBC基於同一思想:根據API編寫的程序可與驅動管理器通信,驅動管理器通過驅動程序與數據庫通信。

那麼ODBC是什麼呢?

ODBC是微軟制定的訪問數據庫的接口標準。ODBC為異構數據庫訪問提供統一接口,並可以使用結構化查詢語言存取數據。

驅動管理器是什麼?

驅動程序管理器為應用程序加載和調用驅動程序,它可以同時管理多個應用程序和多個驅動程序。

JDBC為多種不同的數據庫提供了統一訪問的接口,而每種數據庫對應的驅動程序不盡相同,那麼驅動程序分幾類呢?

1) 驅動程序將JDBC翻譯成ODBC,然後通過ODBC驅動程序與數據庫通信

2) 驅動程序由Java代碼與本地代碼構成,它調用數據庫客戶端API實現與數據庫的通信。客戶端要安裝JDK和JRE,甚至是本地代碼

3) 驅動程序只由Java語言編寫,它使用與具體數據庫無關的協議與數據庫組件通信,然後該組件將此消息翻譯為數據庫相關的協議。

4) 驅動程序只由Java語言編寫,它將JDBC發送給數據庫的消息直接翻譯為數據庫相關的協議。

第3)和4)類驅動程序是目前最常見的。

JDBC

驅動程序部署在哪裡呢?是在客戶端還是在服務端呢?

注意,這裡的服務端指的是數據庫服務器所在的位置。

JDBC一般不位於服務端,即我們要操縱某個數據庫,需要下載特定的驅動程序,然後在我們的應用程序中使用該驅動。

2 JDBC配置

JDBC URL

JDBC採用與URL相似的結構來描述數據源。

JDBC URL的一般形式為:

jdbc:subprotocal:other stuff

jdbc這部分是固定不變的,subprotocal代表具體的驅動程序,other stuff

依據subprotocal不同而不同。

例如:

連接derby:jdbc:derby://localhost:1527/dbname;create=true

連接MySQL:jdbc:mysql://localhost:3306/dbname

連接PostgreSQL:jdbc:postgresql://localhost:5432/dbname

這裡對other stuff部分僅舉簡單示例,還有很多參數供我們選用,詳細的配置可以在官網文檔中找到。

驅動Jar包與Shell

我們還要到數據庫的官網下載驅動文件(當然如果使用Maven管理Jar包的話就不用這麼麻煩),然後添加到我們的項目中。例如,你使用Eclipse作為開發工具,那麼可以將Jar包引進來,但是強烈建議使用Maven管理Jar包。

如下面,開源框架JFoenix的示例代碼就使用了Maven

愉快地學Java語言:第十七章 訪問數據庫

此外,差不多每種數據庫都會提供一些shell命令,不過如果覺得不方便的話可以安裝圖形界面的客戶端。

驅動要向驅動管理器註冊它自己,有些驅動類可以自動註冊,那麼都有哪些呢?

我們可以將下載的Jar包解壓,若有META-INF/services/java.sql.Driver這個文件,那麼這個驅動就可以自動註冊。

對於不能自動註冊的驅動類如何處理呢?

我們要在程序中加入如下代碼:

Class.forName(驅動器類名);

或者

System.setProperty(“jdbc.drivers”,驅動器類名)

不同的數據庫供應商提供了不同的類名,例如:

Derby驅動器類名:org.apache.derby.jdbc.ClientDriver

MySQL驅動器類名:com.mysql.jdbc.Driver

PostgreSQL驅動器類名:org.postgresql.Driver

不過我們一般不必這麼麻煩,大多數驅動程序都是自動註冊的Derby,MySQL,PostgreSQL都是這樣。他們早期提供的驅動版本不是自動註冊,目前的版本都是自動註冊的。

3編程

下面以訪問derby為例。

準備工作

正式開始編程前,讓我們做些準備工作,我打算使用Maven來管理Jar包,所以先配置Maven。

如下圖,我們打開https://search.maven.org/,查找derby的依賴配置。

愉快地學Java語言:第十七章 訪問數據庫

發現有好多選項,那麼該選擇哪一個呢?

應該選GroupId為org.apache.derby的那一個項目,然後選擇一個10.15.1.3這個版本,但是如果你使用的是JDK 1.8那麼沒法用這個版本,因為這個版本要求JDK 1.9或更高版本,所以我使用10.14.2.0。將下面這段配置添加到pom.xml中;

<dependency>

<groupid>org.apache.derby/<groupid>

<artifactid>derby/<artifactid>

<version>10.14.2.0/<version>

愉快地學Java語言:第十七章 訪問數據庫

連接到數據庫

我們使用DriverManager.getConnection這個方法創建數據庫連接,注意這個方法會拋出必檢異常。這個方法的參數是JDBC URL

我們還可以使用DriverManager.setLogWriter方法啟用JDBC的跟蹤特性。還可以在JDBC URL中添加參數來配置這一特性,例如:

jdbc:derby://localhost:1527/dbname;create=true;traceFile=trace.out

執行SQL語句

我們會調用Connection的實例方法createStatement()創建一個Statement。

調用Statement對象的execute方法來執行給定的SQL語句,該語句可能返回多個結果。在某些(不常見)情況下,單個SQL語句可能返回多個結果集或更新計數。通常,可以忽略這一點,除非執行的存儲過程可能返回多個結果,或者動態執行未知的SQL字符串。

調用Statement對象的executeQuery獲得一個ResultSet對象。遍歷ResultSet對象獲得所有行。

要想獲得數據庫的綜合信息可以使用DatabaseMetaData對象。

愉快地學Java語言:第十七章 訪問數據庫

管理連接、語句和結果集

一個Connection對象可以創建多個Statement實例,默認情況下,一個Statement對象可以用於多個不相關的命令或查詢,但只能同時打開一個ResultSet對象。因此,如果同時執行多個查詢操作,那麼必須創建多個Statement對象,一個Statement打開一個ResultSet對象。

調用DatabaseMetaData實例的getMaxStatements方法可以查看同時活動的Statement對象最大數量,如果返回0代表沒有限制或者未知。

Connection、Statement、ResultSet都實現了接口AutoCloseable接口,當使用完以後務必關閉資源,可以使用try語句關閉資源。

PreparedStatement

如果多次執行同一個SQL語句,也就是說,語句大體相同,只是某些參數不同,那麼最好使用PreparedStatement。

我們使用?作為變量的佔位符。

例如:select * from books where BookName =?

愉快地學Java語言:第十七章 訪問數據庫

讀寫LOB

上面介紹的方法都不適合讀寫大對象,要存儲一個大對象怎麼辦?

使用Blob對象代表大文件,然後操作Blob對象將大對象存入數據庫。看下面的例子:

愉快地學Java語言:第十七章 訪問數據庫

愉快地學Java語言:第十七章 訪問數據庫

愉快地學Java語言:第十七章 訪問數據庫

多結果集

有些數據庫允許單個查詢提交多個select語句,這樣一個查詢會返回多個結果集,如何獲得多個結果集呢?

下面給出一個示例,注意:

1)調用execute執行SQL語句,返回結果為true表明第一個結果是結果集,如果是false表明對第一個是更新計數或無結果。

2)調用getMoreResults移動到下一個結果集,返回結果為true表明下一個結果是結果集,如果是false表明對下一個是更新計數或無結果。

3)這裡有一個while循環,什麼時候循環結束呢?遍歷完所有結果的時候循環就該結束,如果SQL執行結果不是結果集且getUpdateCount返回值為-1,那麼就意味著所有結果均遍歷完。

愉快地學Java語言:第十七章 訪問數據庫

獲取自動生成鍵

可以使用ResultSet keys = s.getGeneratedKeys();來獲得自動生成的鍵。

可滾動與可更新結果集

為什麼需要可滾動結果集?

因為要展示結果,總不能將查詢到的結果一次全部展示出來,正確的做法是分頁展示,這樣就需要可滾動結果集。

為了獲得可滾動結果集要使用

createStatement(int resultSetType, int resultSetConcurrency)

prepareStatement(String sql, int resultSetType,

int resultSetConcurrency)

ResultSet中定義了多個常量值可為resultSetType和resultSetConcurrency賦值。

適合於resultSetType的常量

TYPE_FORWARD_ONLY,結果集不能滾動

TYPE_SCROLL_INSENSITIVE,可滾動,但對數據庫變化不敏感

TYPE_SCROLL_SENSITIVE,可滾動,但對數據庫變化敏感

適合於resultSetConcurrency的常量

CONCUR_READ_ONLY,結果集不能用於更新數據庫

CONCUR_UPDATABLE,結果集可用於更新數據庫

ResultSet提供了下面的方法滾動結果集:

previous():向後滾動,當遊標位於有效的行上,返回true,如果遊標位於第一行之前,則返回false。

relative(rows):如果rows大於0,向前移動rows行;小於0,向後移動rows行;等於0,則遊標位於當前行不變。如果rows超出了結果集的範圍,那麼遊標位於當前行不變且方法返回值為false。

使用SQL語句就可以完成更新操作,為什麼需要可更新結果集?

使用可更新結果集的好處在於沒有提交變更之前還可以撤銷更改。

ResultSet提供了多種更新方法,都是以update開頭的,例如updateDouble(int columnIndex, double x),用於更新當前行double型字段值。調用完這類方法後,調用updateRow()將變更提交給數據庫,這樣才會修改數據庫中字段的值;在沒調用updateRow()之前可以調用cancelRowUpdates()撤銷變更。

還可以插入數據,例如

愉快地學Java語言:第十七章 訪問數據庫

行集

可以使用結果集操作數據,那麼為啥要使用行集?

使用結果集的時候,必須始終與數據庫保持連接,而使用行集,可以在斷開與數據庫連接的情況下操作數據。

行集RowSet有多個擴展接口:CachedRowSet,WebRowSet,FilteredRowSet等。

以CachedRowSet為例的創建方法為:

RowSetFactory rsf = RowSetProvider.newFactory();

CachedRowSet cr = rsf.createCachedRowSet();

元數據

元數據是指描述數據庫或其組成的數據。DatabaseMetaData代表了數據庫元數據,它提供了很多獲取數據庫相關信息的方法。

愉快地學Java語言:第十七章 訪問數據庫

還有一種和結果集相關的元數據ResultSetMetaData

愉快地學Java語言:第十七章 訪問數據庫

事物

下面的代碼展示事物操作的一般流程。

愉快地學Java語言:第十七章 訪問數據庫

我們可以設置事物的回滾點,根據業務需要回滾到特定位置,而不是回滾到事物的開始處。

愉快地學Java語言:第十七章 訪問數據庫

如果一次要插入多條記錄到數據庫,一條一條執行SQL語句效率比較低,所以使用批量提交,我們應該將批量提交視為事物。另外還要注意批量提交不支持select語句。

愉快地學Java語言:第十七章 訪問數據庫


分享到:


相關文章: