Trong bài viết này, chúng ta sẽ tìm hiểu về cách sử dụng các API của JDBC để thực thi các câu lệnh truy vấn dữ liệu. JDBC (Java Database Connectivity) cung cấp một cách tiêu chuẩn để kết nối và tương tác với các cơ sở dữ liệu từ lập trình java' class='hover-show-link replace-link-2002'> ngôn ngữ lập trình java .
Chuẩn bị
Trước khi đi vào chi tiết từng JDBC API, chúng ta cần chuẩn bị một cơ sở dữ liệu để sử dụng làm demo. Trong ví dụ này, chúng ta sẽ sử dụng cơ sở dữ liệu MySQL, nhưng bạn có thể sử dụng bất kỳ loại cơ sở dữ liệu nào khác tùy thích.
Sau khi cài đặt cơ sở dữ liệu, chúng ta cần tạo một database và một bảng user với cấu trúc như sau:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `createdDate` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Chúng ta cũng cần tạo một lớp tiện ích để hỗ trợ việc mở kết nối đến cơ sở dữ liệu (DB).
package com.gpcoder; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionUtils { private static final String hostName = "localhost"; private static final String dbName = "jdbcdemo"; private static final String userName = "root"; private static final String password = ""; // jdbc:mysql://hostname:port/dbname private static final String connectionURL = "jdbc:mysql://" + hostName + ":3306/" + dbName; public static Connection openConnection() throws SQLException { // 1. Load Driver // Class.forName("com.mysql.jdbc.Driver"); DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver()); // 2. Open connection return DriverManager.getConnection(connectionURL, userName, password); } }
Statement
Interface Statement trong Java cung cấp các phương thức để thực thi các câu lệnh truy vấn với cơ sở dữ liệu SQL. Statement cung cấp phương thức để tạo ra đối tượng ResultSet.
Theo mặc định, tại cùng một thời điểm chỉ có một đối tượng ResultSet có thể được mở cho mỗi đối tượng Statement. Vì vậy, nếu hoạt động đọc một đối tượng ResultSet bị chen ngang bởi hoạt động đọc đối tượng khác, thì đối tượng khác này phải được tạo bởi đối tượng Statement khác.
Statement cung cấp một số phương thức để thực thi truy vấn SQL:
-
execQuery()
: được sử dụng để thực hiện các truy vấn truy xuất giá trị từ cơ sở dữ liệu (select). Phương thức này trả về đối tượng ResultSet có thể được sử dụng để lấy tất cả các dữ liệu (record) của bảng. -
execUpdate()
: được sử dụng để thực hiện các truy vấn insert/update/delete. -
execute()
: có thể thực thi cả 2 trường hợp trên. Nếu phương thứcstatement.getUpdateCount()
trả về số lượng record bị ảnh hưởng.
import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class executeUpdate { public static void main(String[] args) throws SQLException { try (Connection con = ConnectionUtils.openConnection(); Statement st = con.createStatement();) { // Insert String sqlInsert = "INSERT INTO user(username, password, createdDAte) " + " VALUE('user1', '123', now());"; int numberRowsAffected = st.executeUpdate(sqlInsert); System.out.println("Affected rows after inserted: " + numberRowsAffected); // Update String sqlUpdate = "UPDATE user SET password='123456' WHERE id=1"; numberRowsAffected = st.executeUpdate(sqlUpdate); System.out.println("Affected rows after updated: " + numberRowsAffected); // Delete String sqlDelte = "DELETE FROM user WHERE id=1"; numberRowsAffected = st.executeUpdate(sqlDelte); System.out.println("Affected rows after deleted: " + numberRowsAffected); } } }
ResultSet
ResultSet là một đối tượng Java, nó được trả về khi truy vấn (query) dữ liệu từ cơ sở dữ liệu. Sử dụng resultSet.next()
để di chuyển con trỏ tới các bản ghi (record/row) tiếp theo. Tại một record nào đó, sử dụng các method resultSet.getXxx()
để lấy ra các giá trị tại các cột. Các cột được đánh số với bắt đầu là 1.
Mặc định, khi duyệt dữ liệu trong ResultSet, nó chỉ có thể chạy từ trên xuống dưới, từ trái sang phải. Tuy nhiên, chúng ta có thể thay đổi hướng di chuyển của ResultSet bằng cách chỉ định loại ResultSet sử dụng.
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class StatementExecuteQueryExample { public static void main(String[] args) throws SQLException { String sqlSelect = "SELECT * FROM user"; try (Connection con = ConnectionUtils.openConnection(); Statement st = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet rs = st.executeQuery(sqlSelect);) { while (rs.next()) { showUserInfo(rs); } System.out.println("\n=== Move to previous row ==="); while (rs.previous()) { showUserInfo(rs); } System.out.println("\n=== Move to last row ==="); rs.last(); showUserInfo(rs); System.out.println("\n=== Move to first row ==="); rs.first(); showUserInfo(rs); } } private static void showUserInfo(ResultSet rs) throws SQLException { System.out.println("Id: " + rs.getInt(1)); System.out.println("UserName: " + rs.getString(2)); System.out.println("Password: " + rs.getString("password")); System.out.println("CreatedDate: " + rs.getDate("createdDate")); System.out.println("-"); } }
PreparedStatement
PreparedStatement là một sub-interface của Statement. PreparedStatement được sử dụng để chuẩn bị trước các câu lệnh SQL và tái sử dụng chúng nhiều lần, giúp cho chương trình thực hiện nhanh hơn.
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class PreparedStatementExample { public static void main(String[] args) throws SQLException { String sqlInsert = "INSERT INTO user(username, password, createdDAte) " + " VALUE(?, ?, ?);"; try (Connection con = ConnectionUtils.openConnection(); PreparedStatement pstm = con.prepareStatement(sqlInsert, Statement.RETURN_GENERATED_KEYS);) { // Set parameter values pstm.setString(1, "gpcoder-test-1"); pstm.setString(2, "pwd123"); pstm.setDate(3, new java.sql.Date(System.currentTimeMillis())); // Executes the SQL statement pstm.execute(); // Get generated key try (ResultSet rs = pstm.getGeneratedKeys();) { int idValue = 0; if (rs.next()) { idValue = rs.getInt(1); } System.out.println("Auto-generated id: " + idValue); } } } }
CallableStatement
CallableStatement được xây dựng để gọi một thủ tục (procedure) hoặc hàm (function) của SQL.
package com.gpcoder; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; public class CallableStatementExample { public static void main(String[] args) throws SQLException { String sql = "{call find_user_by_name(?)}"; try (Connection con = ConnectionUtils.openConnection(); CallableStatement cstm = con.prepareCall(sql);) { // Set parameter values cstm.setString(1, "gpcoder1"); // Executes the Procedure statement try (ResultSet rs = cstm.executeQuery();) { while (rs.next()) { showUserInfo(rs); } } } } private static void showUserInfo(ResultSet rs) throws SQLException { System.out.println("Id: " + rs.getInt(1)); System.out.println("UserName: " + rs.getString(2)); System.out.println("Password: " + rs.getString("password")); System.out.println("CreatedDate: " + rs.getDate("createdDate")); System.out.println("-"); } }
Transaction Management
Giao dịch (Transaction) là một khái niệm quan trọng trong SQL. Transaction đảm bảo tính toàn vẹn dữ liệu. Các câu lệnh trong Transaction được gọi là thành công nếu cả câu lệnh thành công. Ngược lại, chỉ cần một trong các câu lệnh lỗi thì coi như giao dịch không thành công, phải rollback lại trạng thái ban đầu.
import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class TransactionExample { public static void main(String[] args) throws SQLException { try (Connection con = ConnectionUtils.openConnection(); Statement st = con.createStatement();) { con.setAutoCommit(false); // 1. Disable individualtransaction try { // This user will be inserted st.executeUpdate("INSERT INTO user(username, password, createdDAte) " + "VALUE('user-1', '123', now());"); System.out.println("Inserted user-1 successfully"); // This is an error sql. Cannot insert user st.executeUpdate("INSERT INTO user2(username, password, createdDAte) " + "VALUE('user-2', '123', now());"); System.out.println("Inserted user-2 successfully"); con.commit(); // 2. commit data to database if all command are success } catch (Exception e) { e.printStackTrace(); con.rollback(); // 2. roll-back data if one of command are failed System.out.println("Rollback data"); } } } }
Batch Processing
Batch Processing là nhóm các lệnh có liên quan vào trong một batch và thực thi chúng. Việc ứng dụng Batch Processing trong cơ sở dữ liệu rất tiện lợi. Khi bạn gửi một số lệnh SQL cùng một lúc, bạn đã giảm được chi phí thời gian giao tiếp và tăng hiệu suất.
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; class User { String username; String password; java.sql.Date createdDate; public User(String username, String password, java.sql.Date createdDate) { this.username = username; this.password = password; this.createdDate = createdDate; } } public class BatchProcessingExample { public static void main(String[] args) throws SQLException { String sqlInsert = "INSERT INTO user(username, password, createdDAte) " + " VALUE(?, ?, ?);"; try (Connection con = ConnectionUtils.openConnection(); PreparedStatement pstm = con.prepareStatement(sqlInsert, Statement.RETURN_GENERATED_KEYS);) { try { con.setAutoCommit(false); List users = readUsersFromCsvFile(); for (User user : users) { pstm.setString(1, user.getUsername()); pstm.setString(2, user.getPassword()); pstm.setDate(3, user.getCreatedDate()); pstm.addBatch(); // Add user to batch } // Executes the SQL statement int[] counts = pstm.executeBatch(); System.out.println("Affected row [0] = " + counts[0]); System.out.println("Affected row [1] = " + counts[1]); con.commit(); } catch (Exception e) { e.printStackTrace(); con.rollback(); } } } private static List readUsersFromCsvFile() { List users = new ArrayList<>(); for (int i = 1; i <= 2; i++) { users.add(new User("user-" + 1, "pwd123", new java.sql.Date(System.currentTimeMillis()))); } return users; } }
SQLException
Trong JDBC, lớp java.sql.SQLException cung cấp rất nhiều phương thức để xử lý các ngoại lệ xảy ra cho cả Driver và Database. Đây là một trong các lớp cơ bản của JDBC, và chịu trách nhiệm về xử lý các ngoại lệ.
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class SQLExceptionExample { private static final String hostName = "localhost"; private static final String dbName = "jdbcdemo"; private static final String userName = "root"; private static final String password = ""; private static final String connectionURL = "jdbc:mysql://" + hostName + ":3306/" + dbName; public static void main(String[] args) { try { Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection(connectionURL, userName, password); Statement st = con.createStatement(); String sqlInsert = "INSERT INTO user(username, password, createdDate) " + " VALUE('gpcoder', '123', now());"; int numberRowsAffected = st.executeUpdate(sqlInsert); if (numberRowsAffected == 0) { System.out.println("Insertion failed"); } else { System.out.println("Inserted successfully: " + numberRowsAffected); } } catch (SQLException se) { se.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
Trên đây là những kiến thức cơ bản về sử dụng JDBC API để thực thi câu lệnh truy vấn dữ liệu. Hy vọng rằng bài viết này đã giúp bạn hiểu rõ hơn về cách sử dụng JDBC để tương tác với cơ sở dữ liệu trong Java.
Nếu bạn muốn tìm hiểu thêm về JDBC API, bạn có thể tham khảo các tài liệu và nguồn tham khảo dưới đây.