看到資料庫連接不由得想起了大一末參加團隊考核時的悲催經歷~~,還記得當初傻傻地按照書本的代碼打到 Eclipse 上,然後一運行就各種報錯。。。報錯後還傻傻地和書本的代碼一遍又一遍地進行核對,發現無誤後,還特別糾結——代碼和書本一樣,怎麼就報錯了呢? 最後通過 Google 才得知要添加驅動包,就這 ...
看到資料庫連接不由得想起了大一末參加團隊考核時的悲催經歷~~,還記得當初傻傻地按照書本的代碼打到 Eclipse 上,然後一運行就各種報錯。。。報錯後還傻傻地和書本的代碼一遍又一遍地進行核對,發現無誤後,還特別糾結——代碼和書本一樣,怎麼就報錯了呢? 最後通過 Google 才得知要添加驅動包,就這樣好多個小時就白白浪費掉了 ~~ 當初連 JDBC 與 JDBC Driver 還沒區分好,往事不堪迴首。。。
大二末,抱著“自己被抗了,也坑坑師弟,讓他們體驗下爬坑的經歷”的心態,考核師弟時故意沒發驅動包給他們,事後聽他們說就因為這坑了他們幾天的時間。。。
大三末,輪到大二師弟考核大一的師弟,然後大二的師弟和大一的說,“資料庫連接時,需要一個驅動包,你們自己去瞭解、下載,具體的就不說了,要學會解決問題。當年帶我的師兄也沒直接提供給我,還害我花了幾天的時間。所以今年我算好了,還提醒你們要註意。。。”,聽到這我背後不由的一涼,這得有多大的怨氣。。。(^_^,這不怪我,叫了你們遇到解決不了的問題可以問師兄,你們沒問而已,逃~~)
好了,回憶結束~~。為了懷念以前的“懵懂青春”,總結下 Java的資料庫連接以及 JDBC、JDBC Driver。
JDBC 與 JDBC Driver
首先要搞清楚的是,什麼是 JDBC,什麼是 JDBC Driver:
-
JDBC,JDBC 的全稱為 Java Database Connectivity,它定義了一套訪問資料庫的 API。使用這些 API 你就可以使用 Java 來操控資料庫,執行
select
、update
、delete
、insert
等常用的操作。(具體定義請看維基百科) -
JDBC Driver,JDBC Driver 就是 Java 與資料庫之間的一層軟體組件——驅動。就像我們的滑鼠驅動、鍵盤驅動等驅動,它擔任了一個中間人、一名翻譯者。因此,要想使用 Java(JDBC API)來訪問操控資料庫,我們還需要 JDBC Driver 來負責翻譯。(維基百科上有具體介紹)
JDBC Driver
JDBC Driver 一般是由相應的資料庫提供的,比如 MySQL 提供了 Connector/J 驅動 (根據官方說明,Connector/J 屬於第 4 種類型的驅動。想瞭解更詳細、更多的驅動類型可以參加維基百科)。
JDBC Driver 的安裝
JDBC Driver 的安裝很簡單,只需要去官網下載它提供的驅動,並把相應的 Jar 包(通常叫作 mysql-connector-java-version-bin.jar)放在 Java Classpath 下就 OK,如果你和我一樣是搞 J2EE 的,你也可以直接把它放在 WEB-INF/lib 目錄下,或者用類似 Maven 之類的工具來添加。詳細請看 官方文檔 。
使用
JDBC Driver 的使用在 這 有很多例子,因此不再累贅了。畢竟它只是一個驅動,我們更多的是使用 JDBC API 調用這個驅動與資料庫打交道。更多的使用總結請看下部分。
JDBC API
JDBC 只是 Java 中定義的一些介面,它也屬於 JDK 的一部分,就像文件等普通介面一樣,我們只需要調用它來完成目的就 OK 。既然是 API 我們就要去看文檔熟悉它才能更好地使用它,這些類分別處於 java.sql 和 javax.sql。
連接
要想使用 JDBC API 來操控資料庫,首先要連上資料庫。連接資料庫有兩種方法:
- 使用 DriverManager 。第一次使用 DriverManager 來建立與資料庫的連接時,它會自動在 class path 中尋找並載入 JDBC 4.0 驅動。要註意的是,如果是 4.0 之前的版本,需要手動去載入。
- 使用 DataSource 。根據官方推薦,我們應該優先使用 DataSource 。相對於比較簡單的 DriverManager ,它比較複雜,也比較全面、詳細。
使用 DriverManager 連接資料庫:
要使用 DriverManager ,我們先要把該類載入進來,使用最簡單的方法(在要載入的類里添加以下代碼)
Class.forName("com.mysql.jdbc.Driver").newInstance();
載入了 DriverManager 後,我們就可以使用它來獲取與資料庫的連接(假設我們使用本地數據,預設路徑為 localhost ;資料庫名為 test ;用戶名為 root;密碼為 123):
// DriverManager、Connection 類在 java.sql 里都有定義
String URL = "jdbc:mysql://localhost:3306/test?user=root&password=123";
Connection conn = DriverManager.getConnection(URL);
//或者
String URL = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "123";
Connection conn = DriverManager.getConnection(URL, user, password);
//或者
String URL = "jdbc:mysql://localhost:3306/test";
Properties connectionProps = new Properties();
connectionProps.put("user", this.userName);
connectionProps.put("password", this.password);
Connection conn = DriverManager.getConnection(URL, connectionProps);
(個人覺得,第一種看起來簡單、方便,但是修改麻煩、可讀性不高,不太建議使用。如果你只想簡單地傳遞用戶名與密碼可以選用第二種。如果你有很多參數要傳遞,比如字元編碼、用戶名密碼等,優先選擇第三種。)
連接資料庫,我們只需調用 getConnection() 方法並且返回與資料庫的連接(Connection)就 OK ,有了此連接我們就可以操作數據了。
在這要註意的是 getConnection 方法里的字元串參數 URL 。該字元串指定了資料庫的路徑、資料庫名、資料庫配置(用戶名密碼等)。 MySQL 的 URL 語法如下:
jdbc:mysql://[host][,failoverhost...][:port]/[database] [?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
詳細說明
使用 DataSource 連接:
DataSource 連接涉及的東西比較多,找時間另開一篇來總結。 ^_^
創建語句
連接上了資料庫,我就可以操控資料庫了。通常我們是編寫 SQL 語句來操控資料庫的。因此,在操控資料庫之前,我們還要創建 SQL 語句。JDBC 定義了 3 種類型語句介面,用來運行 SQL 語句,並且返回執行結果:
- Statement
- PreparedStatement
- CallableStatement
相應地,創建語句也有三種方法:
Connection.createStatement()
——創建普通的語句,它通常不需要提供參數。更多Connection.prepareStatement(String stringSQL)
——創建預編譯語句,通常要提供一個帶有占位符的字元串 SQL 語句。 更多Connection.prepareCall()
——創建存儲過程。更多
這裡要註意的是,Statement
與 PreparedStatement
的區別。它們的最主要區別就是:
PreparedStatement
對 SQL 語句進行了預編譯,在需要運行多次 SQL 語句是能得到顯著的性能提升- 可以有效地防止 SQL 註入攻擊( SQL injection attacks )
- 可以輕鬆地在 SQL 字元串里使用非標準的 Java 對象,如
Date
、Time
、Timestamp
、BigDecimal
等 - 相對於使用拼接方式的 Statement,它使用占位符(?) 很好地把查詢語句以及變數值分開。
- 更多請看 stackoverflow 上的討論
操控資料庫
平時我們操控資料庫,最常用的、最簡單的就是select
、update
、delete
、insert
等常用的操作了。那麼我們是怎麼通過 JDBC API 來實現這些行為的呢?
通常,我們會使用 Statement.executeQuery(String sql)
來執行 select
查詢操作,它會返回一個 ResultSet 的對象,這個對象包含了查詢返回的數據。update
、delete
、insert
等更新操作使用 Statement.executeUpdate(String sql)
,它會返回一個整數,代表影響的行數。
ResultSet
ResultSet 是查詢資料庫時返回的數據集,我們可以把它想象為一張與數據表類似的數據表,就像我們使用命令行執行 select 語句時控制台返回的數據表。不同之處是它擁有一個一開始處於第一行數據的前一行的指針。然後我們就可以通過不停地調用 ResultSet.next()
來移動指針獲取每一行的數據,該函數在指針移動到最後一行的下一行時就返回 false
,我們可以利用這個特性來作為終止條件遍歷整張表。(第一行的前一行與最後一行的下一行都是不存在的行,是虛擬的行)
此外,ResultSet 有 3 中類型(更多):
- TYPE_FORWARD_ONLY:這個是預設的類型,它只能不同地調用
next()
方法把指針向下一行移動,而不能往回走。 - TYPE_SCROLL_INSENSITIVE:這個類型除了可以調用
next()
方法把指針向下一行移動外,還可以調用previous()
方法把指針指向前一行,使用first
方法把指針移動到第一行等。 - TYPE_SCROLL_SENSITIVE:此方法與第 2 個類型一樣,可以任意移動指針。但它是敏感型的。也就是說,當你使用
Statement.executeQuery(String sql)
產生了 ResultSet 後,資料庫中響應的數據發生了改變,它會更新 ResultSet 並保持數據與資料庫一致。而第 2 個類型是非敏感型的,它不會更新 ResultSet。
要指定 ResultSet 的類型,我們只需在創建 Statement 時指定:
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE);
OK,我已經可以在獲取數據表中的任意行了,我們現在來獲取一行數據中的特定列的數據。ResultSet 提供了 getter 方法(getBoolean、getLong、getInt等方法)來獲取當前行的列。這些方法的參數既可以是列的索引號(從 1 開始的整型)也可以是列的別名或者列名(字元串)。比如 ResultSet.getInt(1) 獲取第一列的數據,ResultSet.getFloat("score") 獲取列名為 score 的列