前言
資料庫的重要性不言而喻,不管是什麼系統,什麼應用軟體,也不管它們是 Windows 上的應用程式,還是 Web 應用程式,存儲(持久化)和查詢(檢索)資料都是核心的功能。
大家學習資料庫時,比如 MySQL 這個資料庫管理系統,都是在 CLI(Command Line Interface)上操作資料庫的,現在,我們看看,在 Java Web 中,我們如何使用 Java 去操作資料庫。
JDBC
JDBC(Java Data Base Connectivity)是 Java 操作資料庫的一種規範,也是一種 API(與資料庫系統進行通信的标準的 API),更是一門技術。
JDBC 是由一組用 Java 編寫的類和接口組成,對資料庫的操作提供了基本的方法。但是,對于資料庫細節的操作,那就是由資料庫的廠商實作的。使用 JDBC 操作資料庫,需要資料庫廠商提供的資料庫驅動程式的支援。
那什麼是資料庫驅動程式呢?這個驅動(driver)可以了解成一種可以讓資料庫和 Java 彼此進行互動的程式。
簡單來講,JDBC 提供了一種 API 的規範,告訴各大資料庫廠商按這種規範來實作這些 API 具體的實作代碼。可以從兩個角色的角度來說這個 JDBC。從咱們開發人員的角度來說,JDBC 為我們開發人員提供了統一的操作資料庫的 API,不用管這些 API 的具體實作,專注于 API 的調用;從資料庫廠商的角度來說,JDBC 為他們提供了一套标準的模型接口,都按這個接口去做自己的實作。
如何使用 JDBC?
JDBC 的使用主要有如下幾個步驟:
1.注冊資料庫驅動程式(database driver program)到 JDBC 的驅動管理器中。
在連接配接資料庫之前,需要将資料庫廠商提供的資料庫驅動類注冊到 JDBC 的驅動管理器中,一般是把驅動類加載到 JVM 實作的。
Class.forName("com.mysql.jdbc.Driver");
2.建構資料庫連接配接的 URL。
要與資料庫建立連接配接,那麼就需要建構資料庫連接配接的 URL,這個 URL 由資料庫廠商指定,一般符合一種基本格式,即 JDBC協定+IP位址或域名+端口+資料庫名稱。MySQL 的 URL 是 jdbc:mysql://localhost:3306/dbname
3.擷取連接配接對象(Connection 對象)。
String url = "jdbc:mysql://localhost:3306/dbname";
String username = "root";
String password = "123456";
// Connection 對象的擷取需要借助 DriverManager 對象
Connection conn = DriverManager.getConnection(url, username, password);
4.進行資料庫操作。
編寫 SQL,然後擷取 PreparedStatement 對象,對 SQL 語句進行執行。SQL 語句的參數是可以使用占位符 “?” 代替,再通過 PreparedStatement 對象對 SQL 語句中的占位符進行指派。
Statment 這個單詞的意思在這裡指的就是 SQL 語句。
// 編寫SQL
String sql = "INSERT INTO tb_game(name, price, platform) values(?, ?, ?)";
// 擷取 PreparedStatement 對象
PreparedStatement ps = conn.preparedStatement(sql);
// 給占位符指派
ps.setString(1, "NBA2K");
ps.setDouble(2, 198.0);
ps.setString(3, "Windows");
// 執行 SQL,将這條資料寫入資料庫,傳回影響的行數
int row = ps.executeUpdate();
使用 PreparedStatement 對象對 SQL 語句的占位符參數指派,其參數的下标是從 1 開始的。
5.關閉連接配接
conn.close();
CRUD
新增操作
新增操作,就是上面的插入操作,請看上面。
查詢操作
ResultSet
使用 JDBC 查詢資料,與插入資料的操作流程基本一樣,但是執行查詢操作後需要通過一個對象來接收查詢的結果,這個對象就是 ResultSet (結果集)。
ResultSet 是 JDBC API 中封裝的對象,從資料表中查到的所有記錄都會放在這個集合中。ResultSet 中維護着一個 cursor(遊标)來指向目前的資料行(資料記錄),初始化的時候,這個遊标指向第一行的前一行,可以通過 next() 方法來移動遊标,讓遊标指向下一行。
調用這個 next() 它傳回的是一個布爾值,為 true 說明 ResultSet 中還有下一行的資料,為 false 說明沒有,是以可以結合 while 循環使用這個方法來周遊整個 ResultSet。
// 由于一開始的遊标在第一行的前一行,是以執行 next() 後,遊标就指向第一行的資料了
while (resultSet.next()) {
// 處理結果集中每一行的資料
}
擷取到 ResultSet 對象後,移動了光标指定了資料行,然後通過 ResultSet 對象提供的一系列 getXxxx() 方法來擷取目前行的資料,比如 resultSet.getInt("price") 擷取目前行中字段名為 price 的資料。
預設的 ResultSet 是不可更新的,同時它的遊标隻能一步一步 next 下去,隻能走一遍,不能回到上一行的。說了預設,那說明是可以設定的,通過如下代碼進行設定:
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
// rs 是可以滾動的,也就是遊标走到最後又會回到開頭繼續走,并且它的内容是可以被改變的
查詢
找到價格大于50塊錢的所有遊戲:
String sql = "SELECT id, name, price FROM tb_game WHERE price > ?";
PreparedStatement ps = conn.preparedStatement(sql);
ps.setDouble(1, 50);
// 執行查詢
ResultSet rs = ps.executeQuery(sql);
List<Game> gameList = new ArrayList<>();
// 周遊結果集
while (rs.next()) {
Game game = new Game();
// 擷取目前行中字段名為 id 的資料,并指派到 game 對象中
game.setId(rs.getInt("id"));
game.setName(rs.getString("name"));
game.setPrice(rs.getDouble("price"));
gameList.add(game);
}
System.out.println(gameList);
修改(更新)操作
修改(更新)資料的操作,也是和插入資料的操作是類似的。
更新 ID 為 3 的資料記錄,修改其價格為 298 塊錢。
String sql = "UPDATE tb_game SET price = ? WHERE id = ?";
PreparedStatement ps = conn.preparedStatement(sql);
ps.setDouble(1, 298);
ps.setInt(2, 3);
int row = ps.executeUpdate();
删除操作
同理。
String sql = "DELETE FROM tb_game WHERE id = ?";
PreparedStatement ps = conn.preparedStatement(sql);
ps.setInt(1, 1);
int row = ps.executeUpdate();
分頁查詢
在 Java Web 中資料量非常大的情況下,是不利于将所有資料都展示到一個頁面中的,檢視不友善,又占用系統資源。此時就需要對資料進行分頁查詢,同時,以後的工作中,可以說大部分的業務場景都會涉及到分頁查詢。
在 MySQL 中,分頁可以通過其自身的 LIMIT 關鍵字來實作:
SELECT *
FROM tb_game
WHERE price > 50
ORDER BY price DESC
LIMIT 0, 10; // 從表中下标0開始(第一行的下标為0),限制傳回10條記錄
目前分頁涉及到這樣的兩個參數:目前頁碼和頁面大小。
涉及的 SQL 語句:SELECT * FROM tb_game WHERE price > 50 ORDER BY price DESC LIMIT 目前頁碼, 頁面大小
// 分頁參數
int currentPage = 1, pageSize = 10;
// 分頁 SQL
String sql = "SELECT * FROM tb_game WHERE price > 50 ORDER BY price DESC LIMIT ?, ?";
PreparedStatement ps = conn.preparedStatement(sql);
// 指派
ps.setInt(1, (page - 1) * pageSize);
ps.setInt(2, pageSize);
ResultSet rs = ps.executeQuery();
與此同時,還需要計算擷取的資料的總記錄數,用于計算分頁的總頁數,便于前端傳遞是要哪一頁的資料給後端。
int count = 0;
String sql = "SELECT COUNT(*) FROM tb_game WHERE price > 50";
PreparedStatement ps = conn.preparedStatement(sql);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
// 擷取總記錄數,getInt(1) 是擷取第一列的資料
count = rs.getInt(1);
}
總結
目前在 Java 中通過 JDBC 來操作資料庫,就有幾個固定的步驟,先加載資料庫驅動程式,接着擷取資料庫的連接配接,有了這個連接配接後,才能進行 CRUD 的操作,操作後也可以擷取操作的結果,最後關閉這些資源,比如資料庫連接配接。
不過,在日常的開發中,基本不會用到原生的 JDBC 來操作資料庫,一般我們有多種選擇,可以使用 JdbcTemplate、Hibernate、MyBatis、JPA(Java Persistence API,Java 持久化 API)或者是其他任意的持久化架構。