[Java2_09] More JDBC

2.  More JDBC

2.1  Example 4: Atomic Transaction (Commit and Rollback)

Một 

giao dịch nguyên tử – Atomic Transaction

 là một nhóm các câu lệnh SQL, 

tất cả đều

 thành công hoặc 

không

 thành công. 

Điều này là để ngăn chặn cập nhật một phần vào cơ sở dữ liệu. 

Để quản lý giao dịch trong JDBC, trước tiên chúng tôi vô hiệu hóa commit tự động mặc định (commit mọi câu lệnh SQL), đưa ra một vài câu lệnh SQL và sau đó quyết định có đưa ra một 

commit()

cam kết thay đổi hay 

rollback()

loại bỏ tất cả các thay đổi kể từ lần xác nhận cuối cùng . 

Ví dụ:

 
 

conn.setAutoCommit(false);

ResultSet rset = stmt.executeQuery("select id, qty from books where id in (1001, 1002)"); System.out.println("-- Before UPDATE --"); while(rset.next()) { System.out.println(rset.getInt("id") + ", " + rset.getInt("qty")); }

conn.commit();

stmt.executeUpdate("update books set qty = qty + 1 where id = 1001"); stmt.executeUpdate("update books set qty = qty + 1 where id = 1002");

conn.commit();

rset = stmt.executeQuery("select id, qty from books where id in (1001, 1002)"); System.out.println("-- After UPDATE and Commit --"); while(rset.next()) { System.out.println(rset.getInt("id") + ", " + rset.getInt("qty")); }

conn.commit();

stmt.executeUpdate("update books set qty = qty - 99 where id = 1001"); stmt.executeUpdate("update books set qty = qty - 99 where id = 1002");

conn.rollback();

rset = stmt.executeQuery("select id, qty from books where id in (1001, 1002)"); System.out.println("-- After UPDATE and Rollback --"); while(rset.next()) { System.out.println(rset.getInt("id") + ", " + rset.getInt("qty")); }

conn.commit();

Trong một số triển khai cơ sở dữ liệu, bạn cũng được yêu cầu 

“commit” câu lệnh SELECT statement như vậy. 

Rolling Back in Catch-Clause

Các method 

rollback()

thường được gọi bất cứ khi nào có một lỗi (ví dụ 

SQLException

). 

Do đó, nó nên được đặt trong phần 

catch

Ví dụ,

import java.sql.*;
 
public class JdbcCommitCatchTest {   
   public static void main(String[] args) throws SQLException {
      try (
         Connection conn = DriverManager.getConnection(
               "jdbc:mysql://localhost:8888/ebookshop", "myuser", "xxxx");  
         Statement stmt = conn.createStatement();
      ) {
         try {
             
            

conn.setAutoCommit(false);

stmt.executeUpdate("insert into books values (4001, 'Paul Chan', 'Mahjong 101', 4.4, 4)"); stmt.executeUpdate("insert into books values (4001, 'Peter Chan', 'Mahjong 102', 4.4, 4)");

conn.commit();

} catch(SQLException ex) { System.out.println("-- Rolling back changes --");

conn.rollback();

ex.printStackTrace(); } } } }

Ghi chú:

  1. Trong JDK 7 try-with-resource syntax, bạn không thể truy cập được các tài nguyên đã khai báo trong try (in try) trong mệnh đề catch-clause, e.g. để thực hiện conn.rollback(). (Test it out yourself!) Do đó, chúng ta cần lồng 1 try-catch nữa ở phía dưới try-with-resource để thực hiện conn.rollback(), điều này khá lộn xộn nhờ =)) … nhưng là một sự-lộn-xộn-cần-thiết 😀 để đảm bảo tối ưu hóa tài nguyên và xử lý lỗi tuyệt đối 😀 
  2. Bạn sẽ nhận được một ngoại lệ về “

    duplicate entry on primary key

    “, ví dụ: trong MySQL:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '4001' for key 'PRIMARY'

2.2  Example 5: 

ResultSetMetaData

Mỗi đối tượng 

ResultSet

 được liên kết với một tiêu đề (được gọi 

meta-data

), chứa thông tin về 

ResultSet 

đối tượng, chẳng hạn như số cột, tên và loại cột, v.v. Dữ liệu meta được lưu trữ trong một  đối tượng 

ResultSetMetaData

Bạn có thể sử dụng phương thức 

rset.getMetaData()

để truy xuất đối tượng siêu dữ liệu liên quan của 

ResultSet rset

.

ResultSetMetaData 

là hữu ích trong việc xử lý động 

ResultSet

Bạn có thể truy xuất số lượng cột và sử dụng 

để truy xuất nội dung của một số cột cụ thể trong hàng hiện tại. 

Lưu ý rằng số cột bắt đầu từ 1 (không phải 0)

Ví dụ, 

rset.getXxx(columnNumber)

 
ResultSet rset = stmt.executeQuery("select * from books");
 
ResultSetMetaData rsetMD = 

rset.getMetaData()

; int numColumns =

rsetMD.getColumnCount()

; for (int i = 1; i <= numColumns; ++i) { System.out.printf("%-30s",

rsetMD.getColumnName(i)

); } System.out.println(); for (int i = 1; i <= numColumns; ++i) { System.out.printf("%-30s", "(" +

rsetMD.getColumnClassName(i)

+ ")"); } System.out.println(); while (rset.next()) { for (int i = 1; i <= numColumns; ++i) { System.out.printf("%-30s",

rset.getString(i)

); } System.out.println(); }

Output 


Exercise 1:

 

Nâng cấp 

View

 ebookStore.

(nên backup sourcode trước nha). 

Phần ebookstore bạn đã có những phần Hiển thị dữ liệu (danh sách sách, khách hàng, hoá đơn …). Hãy tìm lại những đoạn select đó, và nâng cấp phần hiển thị sang dạng hàng và cột như ví dụ trên, để thêm đẹp nội dung và rõ thông tin. 

2.3  Example 6: 

DatabaseMetaData

[TODO]

2.4  Example 7: 

PreparedStatement

JDBC cung cấp một lớp được gọi 

PreparedStatement

, cho phép bạn truyền tham số vào câu lệnh SQL và thực thi cùng một câu lệnh SQL nhiều lần. Lệnh

 

PreparedStatement 

là một câu lệnh SQL được biên dịch trước có hiệu quả hơn so với việc sử dụng 

Statement 

nhiều lần. 

Trong một 

PreparedStatement

'?'

biểu thị một người giữ chỗ cho tham số. 

Một tập hợp các 

phương thức có thể được sử dụng để điền vào các tham số. 

Ví dụ,

setXxx(placeHolderNumber, value)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.sql.*;              
 
public class JdbcPreparedStatementTest {   
   public static void main(String[] args) {
      try (
         Connection conn = DriverManager.getConnection(
               "jdbc:mysql://localhost:8888/ebookshop", "myuser", "xxxx");  
 
          
         

PreparedStatement pstmt = conn.prepareStatement( "insert into books values (?, ?, ?, ?, ?)");

PreparedStatement pstmtSelect = conn.prepareStatement("select * from books");

) {

pstmt.setInt(1, 7001); pstmt.setString(2, "Mahjong 101"); pstmt.setString(3, "Kumar"); pstmt.setDouble(4, 88.88); pstmt.setInt(5, 88);

int rowsInserted =

pstmt.executeUpdate()

; System.out.println(rowsInserted + "rows affected.");

pstmt.setInt(1, 7002); pstmt.setString(2, "Mahjong 102");

rowsInserted =

pstmt.executeUpdate()

; System.out.println(rowsInserted + "rows affected."); ResultSet rset =

pstmtSelect.executeQuery()

; while(rset.next()) { System.out.println(rset.getInt("id") + ", " + rset.getString("author") + ", " + rset.getString("title") + ", " + rset.getDouble("price") + ", " + rset.getInt("qty")); } } catch(SQLException ex) { ex.printStackTrace(); } } }

Trong ví dụ này, chúng tôi đã sử dụng hai 

PreparedStatement

s: một cho 

INSERT 

5 tham số, ký hiệu là 

'?'

một số khác 

SELECT 

không có tham số – nhưng bạn có thể sử dụng lại 

SELECT 

hiệu quả hơn.

Khi một tham số đã được xác định cho một lần cho trước 

PreparedStatement

, nó có thể được sử dụng cho nhiều lần thực thi, cho đến khi nó bị xóa bởi một lệnh gọi đến 

pstmt.clearParameter()

Trong ví dụ trên, các tham số thứ 3, 4 và 5 của thứ 2 

PreparedStatement

được đặt trong 1 

PreparedStatement

.

Exercise:

 [TODO]

2.5  Example 8: Batch Processing

JDBC 2.0 (có sẵn trong JDK 1.2) hỗ trợ 

xử lý hàng loạt

 các câu lệnh SQL, để cải thiện hiệu suất. 

Mỗi câu lệnh được thêm vào lô thông qua 

Statement.addBatch()

hoặc 

PreparedStatement.addBatch()

Toàn bộ lô câu lệnh sau đó được gửi đến cơ sở dữ liệu để thực thi thông qua 

executeBatch()

, nó trả về một 

int 

mảng giữ mã trả về của mỗi câu lệnh.

 
conn.setAutoCommit(false);   
 

stmt.addBatch

("insert into books values (8001, 'Java ABC', 'Kevin Jones', 1.1, 99)");

stmt.addBatch

("insert into books values (8002, 'Java XYZ', 'Kevin Jones', 1.1, 99)");

stmt.addBatch

("update books set price = 11.11 where id=8001 or id=8002"); int[] returnCodes =

stmt.executeBatch()

; System.out.print("Return codes are: "); for (int code : returnCodes) { System.out.printf(code + ", "); } System.out.println(); conn.commit();

Bạn cũng có thể sử dụng 

PreparedStatement 

để xử lý hàng loạt:

 
Connection conn = DriverManager.getConnection(......);
 
 
PreparedStatement pstmt = conn.prepareStatement(
   "insert into books values (?, ?, ?, ?, ?)");   
 
conn.setAutoCommit(false);   
 
pstmt.setInt(1, 8003);   
pstmt.setString(2, "Java 123");
pstmt.setString(3, "Kevin Jones");
pstmt.setDouble(4, 12.34);
pstmt.setInt(5, 88);

pstmt.addBatch()

; pstmt.setInt(1, 8004); pstmt.setString(2, "Java 456");

pstmt.addBatch()

;

int[] returnCodes = pstmt.executeBatch();

System.out.print("Return codes are: "); for (int code : returnCodes) System.out.printf(code + ", "); System.out.println(); conn.commit();

Exercise 2: Nâng Cấp Performance eBookStore

Bạn cũng hãy sử dụng 

PreparedStatement 

để xử lý việc thao tác với dữ liệu thay thế Statement trong phiên bản cũ.