Nhập môn Java với JDBC và MySQL (II)

Trong bài trước, chúng ta đã làm quen với một số khái niệm cơ bản về MySQL và JDBC trong Java. Chúng ta cũng đã học cách tạo ra một đoạn mã cho phép thực hiện kết nối đến hệ quản trị cơ sở dữ liệu MySQL. Trong bài này, chúng ta sẽ cùng nhau tìm hiểu một số cách thức truy vấn dữ liệu thông qua JDBC.

Để thực hiện câu lệnh SQL trong một ứng dụng có sử dụng JDBC, bạn hãy tạo ra một đối tượng Statement từ đối tượng Connection của bạn. Đối tượng này chứa một kết nối đơn đến cơ sở dữ liệu. Các đối tượng Statement hỗ trợ phương thức executeUpdate() để đưa vào các câu truy vấn thực hiện chức năng thay đổi cơ sở dữ liệu và không trả lại tập kết quả, và phương thức executeQuery() để tạo ra các câu truy vấn cho phép trả lại tập kết quả. Để minh họa kết quả xử lý dữ liệu chúng ta sử dụng một bảng, animal, bảng này hứa 1 cột id chứa số nguyên và 2 cột chứa các chuỗi, name và category. Câu lệnh MySQL để tạo bảng này trông như sau:

 

CREATE TABLE animal(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),

name CHAR(40),

category CHAR(40)
)

Cột id có thuộc tính AUTO_INCREMENT tức là giá trị của nó tự động tăng thêm mà không cần chúng ta trực tiếp can thiệp, vì MySQL tự động gán các giá trị nối tiếp nhau 1, 2, 3, … mỗi khi có 1 bản ghi được bổ sung vào bảng.

Thực hiện câu truy vấn không trả lại tập kết quả

Đầu tiên chúng ta tạo ra một đối tượng Statement từ đối tượng Connection, và sau đó sử dụng chúng để tạo ra và cung cấp giá trị cho bảng animal. DROP TABLE, CREATE TABLE, UPDATE, DELETE INSERT đều là các câu lệnh thực hiện việc thay đổi cơ sở dữ liệu, cho nên phương thức executeUpdate() là phương thức thích hợp để thực thi chúng. Phương thức này trả lại một số nguyên chỉ số lượng hàng trong cơ sở dữ liệu đã bị tác động sau khi thực hiện câu truy vấn. Trong ví dụ dưới đây, số nguyên này đã được gán vào biến count:

Statement s = conn.createStatement ();
int count;
s.executeUpdate (“DROP TABLE IF EXISTS animal”);
s.executeUpdate ( “CREATE TABLE animal (“ + “id INT UNSIGNED NOT NULL AUTO_INCREMENT,”
+ “PRIMARY KEY (id),”
+ “name CHAR(40), category CHAR(40))”);
count = s.executeUpdate (“INSERT INTO animal (name, category)”
+ “ VALUES”
+ “(‘snake’, ‘reptile’),”
+ “(‘frog’, ‘amphibian’),”
+ “(‘tuna’, ‘fish’),”
+ “(‘racoon’, ‘mammal’)”);
s.close();
System.out.println (count + “ dong da duoc tao ra”);

Ở đây, biến count được sử dụng để báo lại số lượng hàng mà câu lệnh INSERT đã bổ sung vào bảng animal.

Một đối tượng Statement có thể sử dụng để tạo ra nhiều câu truy vấn. Khi đã thực hiện xong các thao tác trên cơ sở dữ liệu, bạn hãy gọi phương thức close()để xóa đối tượng và giải phóng tất cả các tài nguyên liên kết đến nó.

Nếu như bạn muốn biết câu lệnh SQL có trả lại tập kết quả hay không (ví dụ như khi người sử dụng nhập câu lệnh vào trường nhập liệu của form), thì bạn có thể dùng phương thức execute() của đối tượng Statement. Phương thức này trả lại true nếu câu lệnh có trả lại tập kết quả. Trong trường hợp đó, đối tượng ResultSet có thể được thu hồi thông qua phương thức getResultSet() và số lượng hàng được tác động có thể biết được thông qua phương thức getUpdateCount():

Statement unknownSQL = con.createStatement();
if(unknownSQL.execute(sqlString)) {
ResultSet rs = unknownSQL.getResultSet();
// hiển thị kết quả
}
else {
System.out.println(“Cac dong da cap nhat: “ + unknownSQL.getUpdateCount());
}

Thực thi các câu truy vấn có trả lại một tập kết quả

Statement s = conn.createStatement ();
s.executeQuery (“SELECT id, name, category FROM animal”);
ResultSet rs = s.getResultSet ();
int count = 0;
while (rs.next ()){
int idVal = rs.getInt (“id”);
String nameVal = rs.getString (“name”);
String catVal = rs.getString (“category”);
System.out.println ( “id = “ + idVal + “, name = “ + nameVal + “, category = “ + catVal);
++count;
}
rs.close ();
s.close ();
System.out.println (count + “ dong duoc thu ve”);

Phương thức executeQuery() không trả lại số nguyên đếm số hàng mà nó tác động, do vậy nếu bạn muốn biết một tập kết quả chứa bao nhiêu hàng thì bạn cần tự mình đếm lấy khi bạn thực thi thao tác lấy dữ liệu trên từng hàng. Để lấy được các giá trị của cột trên từng hàng, thì bạn hãy gọi các phương thức getXXX(),trong đó XXX đại diện cho kiểu giá trị của cột. Chẳng hạn, các phương thức getInt()getString() được sử dụng trong ví dụ trên trả lại các giá trị chuỗi và số nguyên. Như đã trình bày, các phương thức này có thể được gọi bằng cách sử dụng tên của một cột nằm trong tập kết quả. Bạn cũng có thể lấy các giá trị về từ vị trí của chúng. Đối với tập kết quả lấy về từ câu truy vấn SELECT trong ví dụ trên, id, name, và category nằm ở các vị trí cột 1, 2 và 3 và do vậy có thể lấy về theo cách sau:

int idVal = rs.getInt (1);

String nameVal = rs.getString (2);

String catVal = rs.getString (3);

String nameVal = rs.getString (“name”);
if (rs.wasNull ())
nameVal = “(khong ten)”;

Sử dụng thành phần giữ chỗ (Placeholders)

Thỉnh thoảng bạn cần xây dựng một câu truy vấn với các giá trị có chứa các kí tự cần được xử lý đặc biệt. Ví dụ, trong các câu truy vấn, các giá trị dạng chuỗi được viết bên trong các dấu trích dẫn đơn, nhưng bất cứ kí tự trích dẫn đơn nào nằm trong chính chuỗi đó đều cần phải được biến thành dấu trích dẫn kép hoặc phải được chuyển vị (escaped) bằng kí tự ‘’ để tránh tạo ra các câu SQL hoạt động sai mục đích. Trong trường hợp này, cách dễ dàng hơn cả là bạn hãy để JDBC xử lý vấn đề chuyển vị này cho bạn, chứ không nên tự mình làm lấy. Để làm được điều đó, bạn hãy tạo ra một kiểu lệnh khác (một lệnh PreparedStatement), tham chiếu đến các giá trị dữ liệu nằm trong chuỗi truy vấn dưới hình thức các kí tự giữ chỗ. Sau đó báo cho JDBC biết để liên kết các giá trị dữ liệu đó vào các kí tự giữ chỗ này và nó sẽ xử lý một cách tự động bất cứ một kí tự đặc biệt nào. Giả sử bạn có hai biến có tên là nameVal và catVal dùng để chứa các giá trị sẽ chèn vào trong bảng animal. Để làm được điều đó mà không cần quan tâm đến việc liệu các biến đó có chứa các kí tự đặc biệt hay không, bạn hãy tạo ra một câu truy vấn kiểu như thế này:

PreparedStatement s;
s = conn.prepareStatement ( “INSERT INTO animal (name, category) VALUES(?,?)”);
s.setString (1, nameVal);
s.setString (2, catVal);
int count = s.executeUpdate ();
s.close ();
System.out.println (count + “ dong da duoc chen”);

Các kí tự ‘?’ nằm trong chuỗi truy vấn đóng vai trò là các kí tự giữ chỗ–các kí tự đánh dấu đặc biệt nhằm xác định nơi các giá trị dữ liệu cần được đặt vào đó. Phương thức setString() nhận thông tin về vị trí của kí tự giữ chỗ và một giá trị dạng chuỗi rồi gắn kết giá trị đó vào kí tự giữ chỗ thích hợp, thực thi bất cứ thao tác chuyển vị nào cần thiết. Phương thức mà bạn sử dụng để gắn kết một giá trị tùy thuộc vào kiểu dữ liệu. Ví dụ, setString() gắn giá trị dạng chuỗi và setInt() gắn các giá trị dạng số nguyên.

Xử lý lỗi

Nếu bạn muốn bẫy lỗi, hãy thực thi các thao tác JDBC bên trong khối try và sử dụng một bộ xử lý lỗi ngoại lệ để hiển thị thông tin về nguyên nhân gây ra vấn đề có thể xuất hiện. JDBC cung cấp các phương thức getMessage()getErrorCode() mà khi lỗi xuất hiện bạn có thể sử dụng để biết được thông báo lỗi và mã lỗi dạng số. Ví dụ sau cố tình dùng một câu truy vấn mắc lỗi. Khi thực thi, phương thức executeQuery() sẽ không thể xử lý được và phát sinh một lỗi ngoại lệ. Lỗi này sẽ được thu về thông qua khối catch, trong khối này bạn có thể chứa mã lệnh để xử lý lỗi hay chỉ đơn giản là hiển thị thông báo lỗi và mã lỗi:


try{
Statement s = conn.createStatement ();
s.executeQuery(“XYZ”); // tạo ra câu truy vấn không hợp lệ
s.close();
}
catch(SQLException e){
System.err.println (“Thong bao loi: “ + e.getMessage());
System.err.println (“Ma loi: “ +e.getErrorCode());
}

Các đối tượng ResultSet, cũng như các đối tượng Statement, nên được đóng lại khi bạn đã dùng xong chúng. Để kiểm tra xem liệu giá trị của một cột nào đó có là NULL hay không, bạn hãy gọi phương thức wasNull() của đối tượng chứa tập kết quả sau khi lấy giá trị đó về. Ví dụ, bạn có thể kiểm tra giá trí NULL nằm trong cột name như sau:

Các câu lệnh truy vấn có trả lại tập kết quả là các câu lệnh giúp chúng ta lấy ra dữ liệu từ cơ sở dữ liệu dưới một dạng thức nào đó. Ví dụ câu lệnh SELECT lấy thông tin từ một cơ sở dữ liệu về thì khi đó ta dùng phương thức executeQuery(). Sau khi gọi phương thức này, bạn hãy tạo ra một đối tượng ResultSet và sử dụng nó để lặp lại các thao tác dữ liệu trên các hàng mà câu truy vấn trả về. Ví dụ sau cho thấy cách sử dụng phương thức này để lấy thông tin từ bảng animal về: