Khi tìm nạp danh sách các bản ghi thông qua các truy vấn, chúng ta thường cần lưu trữ chúng trong một đối tượng cho phép duyệt qua lại, cập nhật khi cần thiết. Bài viết này minh họa kỹ thuật thường cần thiết này trong lập trình cơ sở dữ liệu với các mã rõ ràng và các tình huống ví dụ.
Giới thiệu về ResultSet
Bộ kết quả là giao diện được xác định trong java.sql bưu kiện. Nó đại diện cho một bảng dữ liệu được trả về bởi một Tuyên bố vật. A Tuyên bố đối tượng được sử dụng để thực thi các truy vấn SQL đến cơ sở dữ liệu. Đối tượng ResultSet duy trì một con trỏ trỏ đến bản ghi hiện tại trong bảng cơ sở dữ liệu. Do đó, nó có thể được sử dụng hiệu quả để định vị ở các hàng khác nhau, qua lại bằng cách sử dụng first () , trước () , tiếp theo () và cuối cùng () các phương pháp theo yêu cầu. Ban đầu, ResultSet đối tượng được định vị tại một vị trí trước hàng đầu tiên. Đây là lý do tại sao ResultSet đường ngang luôn bắt đầu như sau:
while(resultSet.next()) { // ... }
Lưu ý rằng Bộ kết quả đối tượng được định vị ở hàng đầu tiên bằng cách thực thi next () khi vào vòng lặp, bởi vì, như đã được đề cập, ResultSet đối tượng ban đầu nằm ở vị trí ngay trước hàng đầu tiên. Vì vậy, nó phải được đặt ít nhất là hàng đầu tiên, để có được một bản ghi hợp lệ. Nó có thể được coi là giá trị -1 trong một vị trí mảng mà con trỏ / chỉ mục đang trỏ đến. Trước tiên, nó phải được chuyển đến ít nhất là vị trí 0 để nhận được bất kỳ loại giá trị hợp lệ nào từ mảng.
Bây giờ, như chúng tôi đã đề cập, chúng tôi có thể cuộn qua các bản ghi với sự trợ giúp của ResultSet vật. Tuy nhiên, khả năng này không đến theo mặc định. Hành vi mặc định của ResultSet đối tượng là nó không thể cập nhật được và con trỏ mà nó sở hữu thực sự di chuyển theo một hướng, chỉ về phía trước. Điều này có nghĩa là chúng ta chỉ có thể lặp lại các bản ghi một lần và theo hướng về phía trước. Tuy nhiên, có nhiều cách để làm cho nó linh hoạt để Bộ kết quả không chỉ có thể cập nhật mà còn có thể cuộn.
Chúng ta sẽ thấy chúng sau một phút nữa trong hai chương trình riêng biệt.
Bộ kết quả có thể cuộn được
Đầu tiên chúng ta hãy tạo Bộ kết quả đối tượng có thể cuộn. Có thể cuộn được có nghĩa là một khi ResultSet đối tượng đã được tạo, chúng ta có thể duyệt qua các bản ghi đã tìm nạp theo bất kỳ hướng nào, tiến và lùi, tùy thích. Điều này cung cấp khả năng đọc bản ghi cuối cùng, bản ghi đầu tiên, bản ghi tiếp theo và bản ghi trước đó.
package org.mano.example; import java.sql.*; public class App { static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; static final String DB_URL = "jdbc:mysql://localhost:3306/employees"; static final String USER = "root"; static final String PASS = "secret"; static final String SQL = "SELECT * FROM employees ORDER BY first_name"; public static void main( String[] args ) { Connection connection = null; ResultSet rs = null; try { Class.forName(JDBC_DRIVER); connection = DriverManager.getConnection (DB_URL, USER, PASS); System.out.println("n1. Connection established"); }catch(Exception ex) { ex.printStackTrace(); } try (PreparedStatement pstmt = connection.prepareStatement(SQL, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);){ System.out.println("n2. Executing SQL query..."); rs = pstmt.executeQuery(); System.out.println("n3. ResultSet object created successfully."); System.out.println("n4. Now some RecordSet scrolling starts..."); rs.first(); show(rs); rs.last(); show(rs); rs.previous(); rs.previous(); show(rs); rs.next(); show(rs); System.out.println("nn5. That's all. RecordSet scrolling ends."); }catch(SQLException ex){ ex.printStackTrace(); }finally{ try { connection.close(); }catch(SQLException ex){ } } } public static void show(ResultSet rs) throws SQLException{ System.out.printf ("n--------------------------------"+ "-------------------------------------"); System.out.printf("n%7d | %10s | %10s | %s | %s | %s ",rs.getLong("emp_no"), rs.getString("first_name"), rs.getString("last_name"), rs.getDate("birth_date").toString(), rs.getDate("hire_date"), rs.getString("gender")); System.out.printf ("n---------------------------------"+ "------------------------------------"); } }
Đầu ra
- Kết nối được thiết lập.
- Đang thực thi truy vấn SQL…
- Đã tạo thành công đối tượng ResultSet.
- Bây giờ, một số thao tác cuộn RecordSet bắt đầu…
------------------------------------------------------------- 497615 | Aamer | McDermid | 1954-11-18 | 1985-04-24 | M ------------------------------------------------------------- ------------------------------------------------------------- 484995 | Zvonko | Lakshmanan | 1964-11-04 | 1992-12-04 | M ------------------------------------------------------------- ------------------------------------------------------------- 482000 | Zvonko | Cannata | 1960-11-23 | 1986-08-13 | M ------------------------------------------------------------- ------------------------------------------------------------- 483497 | Zvonko | Pollacia | 1961-12-26 | 1985-08-01 | M -------------------------------------------------------------
- Đó là tất cả. Quá trình cuộn RecordSet kết thúc.
Lưu ý rằng ResultSet có thể cuộn được đối tượng là kết quả của việc thực thi executeQuery () phương pháp thu được thông qua bản sao của Statement hoặc Chuẩn bị sẵn sàng . Loại ResultSet đối tượng chúng tôi muốn tạo phải được khai báo rõ ràng cho Tuyên bố đối tượng thông qua các hằng số kiểu cuộn đã xác định.
- ResultSet.TYPE_FORWARD_ONLY: Đây là loại mặc định.
- ResultSet.TYPE_SCROLL_INSENSITIVE: Cho phép di chuyển qua lại nhưng không nhạy cảm với ResultSet cập nhật.
- ResultSet.TYPE_SCROLL_SENSITIVE: Cho phép di chuyển qua lại nhưng nhạy cảm với ResultSet cập nhật.
Có các hằng số khác được sử dụng, chẳng hạn như CONCUR_READ_ONLY , có nghĩa là Bộ kết quả không thể cập nhật. Có một hằng số khác, CONCUR_UPDATABLE , biểu thị điều ngược lại, có nghĩa là Bộ kết quả có thể cập nhật được.
Có thể cập nhật ResultSet
Tạo ResultSet có thể cập nhật có nghĩa là bản ghi mà nó trỏ tới không chỉ có thể duyệt mà còn có thể cập nhật được. Các thay đổi sẽ ngay lập tức được lưu lại trong cơ sở dữ liệu và được phản ánh bởi ResultSet đối tượng trong thời gian thực.
package org.mano.example; import java.sql.*; public class App { static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; static final String DB_URL = "jdbc:mysql://localhost:3306/employees"; static final String USER = "root"; static final String PASS = "secret"; static final String SQL = "SELECT * FROM employees WHERE emp_no = ?"; public static void main( String[] args ) { Connection connection = null; ResultSet rs = null; long emp_no = 484995; try { Class.forName(JDBC_DRIVER); connection = DriverManager.getConnection (DB_URL, USER, PASS); System.out.println("n1. Connection established"); }catch(Exception ex) { ex.printStackTrace(); } try(PreparedStatement pstmt = connection.prepareStatement(SQL, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);){ pstmt.setLong(1,emp_no); System.out.println("n2. Executing SQL query..."); rs = pstmt.executeQuery(); System.out.println("n3. ResultSet object created successfully."); while(rs.next()){ show(rs); String fname = rs.getString("first_name"); System.out.println("n4. Updating name "+fname+" to Subham"); rs.updateString("first_name", "Subham"); rs.updateRow(); } System.out.println("nn5. Record updated. See below."); rs.previous(); show(rs); }catch(SQLException ex){ ex.printStackTrace(); }finally{ try { rs.close(); connection.close(); }catch(SQLException ex){ } } } public static void show(ResultSet rs) throwsSQLException{ System.out.printf ("n--------------------------------"+ "-------------------------------------"); System.out.printf("n%7d | %10s | %10s | %s | %s | %s ",rs.getLong("emp_no"), rs.getString("first_name"), rs.getString("last_name"), rs.getDate("birth_date").toString(), rs.getDate("hire_date"), rs.getString("gender")); System.out.printf ("n---------------------------------"+ "------------------------------------"); } }
ResultSet có thể cập nhật đặc biệt hữu ích khi chúng ta muốn cập nhật các giá trị nhất định sau khi thực hiện một số so sánh bằng cách duyệt qua lại các bản ghi đã tìm nạp. Quá trình tạo tương tự như chương trình trước đó, nhưng ResultSet hằng số được sử dụng ở đây là TYPE_SCROLL_SENSITIVE và CONCUR_UPDATABLE .
Kết luận
Trái ngược với hành vi mặc định của ResultSet, nó cho phép đối tượng có tính linh hoạt cao hơn. Chức năng này có thể được ứng dụng tận dụng để không chỉ duyệt qua các bản ghi mà còn làm cho chúng có thể cập nhật để chúng có thể cung cấp dịch vụ tốt hơn. Mặc dù hành vi tiêu chuẩn của tập hợp kết quả có vẻ không hiệu quả so với Tập kết quả có thể cuộn , nó có công dụng riêng và do đó không thể thay thế được.