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ú:
- 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 :D để đảm bảo tối ưu hóa tài nguyên và xử lý lỗi tuyệt đối :D
- 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ũ.
Đăng nhận xét