You are on page 1of 19

Nhập môn JDBC

JDBC

1.JDBC là cái gì?

JDBC (viết tắt của cụm: "Java Database Connectivity") là kiến trúc, các đặc tả, và giao diện ứng dụng dùng để truy cập
dữ liệu. JDBC API là một giao diện lập trình sử dụng SQL, nó định nghĩa các lớp Java trừu tượng hóa các kết nối CSDL,
các câu lệnh SQL, các tập hợp kết quả, các siêu dữ liệu ... Nó cho phép một người lập trình Java đưa ra các câu lệnh
SQL và xử lý các kết quả được trả về.

2. Tại sao có JDBC?

JDBC ra đời để người viết phần mềm bằng Java có một data source access API. JDBC tách các nhà phát triển ứng dụng
khỏi sự phức tạp của việc kết nối tới một nguồn dữ liệu. Tiêu chí của JDBC là nó phải dễ dàng cho người lập trình ứng
dụng có thể tạo ra các kết nối của người sử dụng cuối tới nguồn dữ liệu thích hợp mà không phải trở thành một chuyên
gia về mạng. Các nhà phát triển Java có thể tạo nên các ứng dụng truy xuất cơ sở dữ liệu mà không cần phải học và sử
dụng các API độc quyền do các công ty sản xuất phần mềm khác nhau bên thứ ba cung cấp. Bạn chỉ cần học JDBC và
sau đó bạn sẽ được đảm bảo rằngbạn sẽ có thể phát triển nên các ứng dụng truy cập cơ sở dữ liệu có khả năng truy cập
đến các RDBMS khác nhau bằng cách sử dụng các JDBC driver khác nhau.

3. Kiến trúc của một hệ thống phần mềm dùng JDBC.

JDBC được xây dựng trên mô hình kiến trúc Client/Server.

Trong kiến trúc Client/Server bao gồm một một client, một server, và một data protocol mà nó cho phép client và server
giao tiếp với nhau. Mô hình này rất lý tưởng cho một Traditional Relational DBMS, trong đó một mạng vật lý kết nối client
PC tới DBMS ở trên một máy khác.

Các mô hình hoạt động của JDBC:

- Mô hình 2 lớp (Two-tier Model):

Đây là một mô hình kinh điển trong kiến trúc Client/Server. Các Two-Tier driver trực tiếp gửi và nhận thông tin trên giao
thức truyền dữ liệu của một DBMS hoặc ánh xạ tới các native Database API, không trực tiếp truy xuất dữ liệu. DBMS
Server nhận các yêu cầu SQL từ Client, thực hiện chúng và gửi kết quả trở lại Client.

- Mô hình 3 lớp (Three-tier Model)

Client trong mô hình Three-Tier thay vì kết nối trực tiếp tới DBMS, nó được kết nối qua một "lớp trung gian" (middle tie) là
Gateway Server, sau đó lớp trung gian sẽ gửi lệnh tới nguồn dữ liệu. Nguồn dữ liệu xử lý lệnh và trả kết quả về cho lớp
trung gian, tiếp theo lớp trung gian gửi chúng (kết quả) cho ứng dụng. Trong thực tế, Gateway Server sẽ kết nối tới nhiều
DBMS. Mô hình Three-Tier đã đưa hầu hết những sự phức tạp trên Client lên Server. Nó trợ giúp rất nhiều trong việc đơn
giản hoá sự cài đặt, quản lý các driver trên Client.

Kiến trúc JDBC


Trong Java có 2 lớp chủ yếu chịu trách nhiệm về thiết lập kết nối đến một cơ sở dữ liệu.

- Lớp đầu tiên là DriverManager. Đó là một trong rất ít các lớp thực sự do JDBC API cung cấp. DriverManager chịu
trách nhiệm quản lý một nhóm (pool) các driver đã đăng kí, mà thực chất là là trừu tượng hóa các chi tiết về việc sử dụng
một driver, cho nên lập trình viên không cần phải làm việc trực tiếp với driver đó. Như tên gọi của nó, nhiệm vụ của nó là
quản lý sự tương tác giữa các chương trình ứng dụng và các driver, nhiều ứng dụng và nhiều driver có thể được quản lý
cùng một lúc. Driver Manager cung cấp sự liên kết giữa các ứng dụng và các driver, cho phép nhiều ứng dụng truy xuất
dữ liệu qua nhiều driver. Driver Manager load hay unload một hoặc nhiều driver cho một hoặc nhiều ứng dụng. Khi một
ứng dụng cần truy xuất một nguồn dữ liệu, Driver Manager sẽ load đúng driver cần thiết.

- Lớp thứ 2 là lớp JDBC Driver. Nó được cung cấp bởi các nhà sản xuất phần mềm độc lập. Lớp JDBC Driver chịu trách
nhiệm thiết lập đường kết nối cơ sở dữ liệu và xử lý tất cả các giao tiếp với cơ sở dữ liệu đó: đưa ra các yêu cầu SQL để
chỉ định các nguồn dữ liệu, và trả về kết quả cho các ứng dụng. Các driver cũng đảm nhận việc tương tác với bất cứ các
lớp phần mềm nào cần thiết để truy xuất nguồn dữ liệu. Các JDBC driver chia thành 4 kiểu khác nhau:

o JDBC Driver loại 1- Chúng là các trình điều khiển cầu nối JDBC-ODBC. Chúng ủy nhiệm công việc truy
cập dữ liệu cho ODBC API. Chúng là trình điều khiển chậm nhất trong số còn lại. SUN cung cấp một
phần mềm trình điều khiển JDBC/ODBC.
o JDBC Driver loại 2 – Chúng chủ yếu sử dụng API mã nền để truy cập dữ liệu và cung cấp các lớp bao
Java để có thể được gọi ra bằng cách dùng các JDBC driver.
o JDBC Driver loại 3 – Chúng được viết thuần bằng Java và sử dụng giao thức Net độc lập nhà sản xuất
để truy cập đến trình theo dõi từ xa độc lập nhà sản xuất. Trình theo dõi này đến lượt nó lại ánh xạ các
lời gọi độc lập nhà sản xuất này vào các lời gọi phụ thuộc nhà sản xuất. Bước đặc biệt này đã làm
tăng độ phức tạp và giảm tính hiệu quả trong truy cập cơ sở dữ liệu.
o JDBC Driver loại 4 – Chúng được viết thuần túy bằng Java và là loại hiệu quả nhất. Chúng cho phép
kết nối trực tiếp vào cơ sở dữ liệu, cung cấp kết quả tối ưu và cho phép lập trình viên thực hiện các
chức năng tùy thuộc vào cơ sở dữ liệu cụ thể. Điều này đã tạo ra tính cơ động cao nhất là khi bạn cần
thay đổi cơ sở dữ liệu bên dưới một ứng dụng. Loại driver này thường được dùng cho các ứng dụng
phân tán cao.

SUN khuyến cáo sử dụng và phát triển các trình điều khiển loại 4 trong các ứng dụng .

JDBC API (các gói java.sql & javax.sql )

JDBC API (API - Application Programming Interface) chứa một tập hợp các lớp, các giao diện Java và các exception. Đa
số các API này được định nghĩa trong gói java.sql tuy nhiên sau đó Sun đưa vào thêm các API khác có tính năng cao cấp
hơn có trong gói javax.sql. Sau đây là các lớp và giao diện quan trọng trong gói java. sql:

1. DriverManager - Nạp các JDBC driver vào trong bộ nhớ. Có thể sử dụng nó để mở các kết nối tới một nguồn dữ
liệu. Tạo sẵn cơ chế phân chia đường kết nối (built-in connection pooling).

2. Connection - Biểu thị một kết nối đến một nguồn dữ liệu. Được dùng để tạo ra các đối tượng Statement,
PreparedStatement và CallableStatement.

3.Statement – Biểu diễn một lệnh SQL tĩnh. Có thể sử dụng nó để thu về đối tượng ResultSet.

4. PreparedStatement – Một giải pháp thay thế hoạt động tốt hơn đối tượng Statement, thực thi một câu lệnh SQL đã
được biên dịch trước.

5.CallableStatement – biểu diễn một thủ tục được lưu trữ. Có thể được sử dụng để thực thi các thủ tục được lưu trữ
trong một RDBMS có hỗ trợ chúng.

6. ResultSet - biểu diễn một tập kết quả trong cơ sở dữ liệu tạo ra bởi việc sử dụng một câu lệnh SQL là SELECT.

7.SQLException - một lớp xử lý lỗi ngoại lệ chứa các lỗi truy cập cơ sở dữ liệu.
Gói thứ hai, javax.sql là một bộ phận của J2SE 1.4 và J2EE 1.3. Nó bổ sung các tính năng sau đây vào JDBC để hỗ
trợ thêm cho các tính năng đã có trong gói java.sql:

8. Data sources - bao gồm các tập hợp dữ liệu và các môi trường tương ứng của chúng, bao gồm các hệ điều hành,
các DBMS, và các phần mềm mạng.

19. XADataSource, XAConnection – Cho phép/Hỗ trợ các giao dịch phân phối.

10. RowSet – là wrapper của ResultSet hỗ trợ cuộn, them, xóa, sửa và phân trang. Một ưu điểm nữa của RS2(Rowset)
là nó chính là 1 Bean Component do đó bạn có thể dùng nó để đăng ký với bất kỳ thành phần GUI(Graphics User
Interface) nào và có thể thông qua đó kiểm soát các event phát sinh. CachedRowSet(CRS) cho phép cached lại dữ liệu
trong memory của client và cho phép thực thi các thao tác động lập với Database server.

Đọc tìm hiểu về JDBC bạn phải có một kiến thức SQL nhất định (bài học này không đi sâu về các ngữ pháp SQL
thông thường). Có rất nhiều các kiểu cơ sở dữ liệu khác nhau như Access, SQL Server ,Oracle hoặc Pointbase ...
sử dụng ngôn ngữ SQL. Và mục tiêu các API của java phải hướng tới là làm thế nào dịch các mệnh lệnh của java
thành mệnh lệnh mà kiểu cơ sở dữ liệu đang làm việc hiểu được và nó sẽ truy vấn dữ liệu sau đó trả về cho ứng
dụng.

Database URL
Địa chỉ của cơ sở dữ liệu (Có thể nằm trên mạng)( Database URL). Được sử dụng để tạo kết nối tới cơ sở dữ liệu. Có
thể chứa tên máy chủ,cổng,giao thức,... (server, port, protocol ...)
Công thức tổng quát : jdbc:subprotocol_name:driver_dependant_info
VD:
– Cầu nối ODBC
• jdbc:odbc:COREJAVA
– Với Oracle
• jdbc:oracle:thin:@machinename:1521:dbname
– Pointbase
• jdbc:pointbase:server://localhost/sample

Để làm việc với cơ sở dữ liệu phải tạo các kết nối tới chúng như vậy cần phải khai báo vị trí của cơ sở dữ liệu, có thể là
ngay trên máy tính, hoặc nằm một nơi nào đó trên mạng vậy cần phải cung cấp địa chỉ URL của nó. Vì vậy khi phát triển
cho data base cụ thể ví dụ như Oracle hoặc Pointbase bạn phải xem qua tài liệu của chúng để có chi tiết cách dùng.

Tóm lại bạn phải cung cấp một String mô tả địa chỉ vị vị trí nguồn cơ sở dữ liệu đó ,đoạn text này có thể chứa thông tin
tên máy server,cổng,giai thức mạng … ví dụ :"jdbc:oracle:thin:@machinename:1521:myDbname"

Định dạng chung cho khai báo đoạn text này jdbc:subprotocol_name:driver_dependant_info trong đó jdbc bao giờ cũng
có , sau đó subprotocol_name là tên của giao thức con , sau đó là thông tin về điều khiển của loại cơ sở dữ liệu
(driver_dependant_info).

MySQL và JDBC

Cài đặt MySQL

MySQL có nhiều phiên bản cho các hệ điều hành khác nhau: phiên bản Win32 cho các hệ điều hành dòng Windows,
Linux, Mac OSX... Connector/J - JDBC Driver của MySQL JDBC Driver của MySQL có thể lấy về ở địa chỉ
http://www.mysql.com/downloads/api-jdbc.html .

File bạn tải về sẽ là một file .zip hoặc .gz. Sau khi giải nén bạn sẽ có 1 file có tên tương tự như sau: mysql-
connectorjava- 5.1.8-stable-bin.jar. Hãy chép file này vào thư mục %JAVA_HOME%/jre/lib/ext (thư mục extension của
jre) trên hệ thống của bạn. Ví dụ C:\Program Files\Java\jre6\lib\ext
Sau khi bạn đã đặt đường dẫn đến Connector/J (file mysql-connectorjava- 5.1.8-stable-bin.jar) vào biến môi trường
MYSQL_DRIVER và CLASSPATH thì bạn đã có thể yên tâm là JDBC driver của MySQL đã được cài đặt xong.

Kiểm tra xem Connector/J đã hoạt động chưa?

Chúng ta tạo ra một chương trình Java nhỏ để kiểm tra xem chúng ta đã cài đặt đúng JDBC driver của MySQL chưa.
Nếu chương trình chạy thành công thì nghĩa là trình điều khiển JDBC đã sẵn sàng cho các tác vụ phức tạp hơn. Bạn
hãy tạo ra một file Connect.java với đoạn mã sau:

import java.sql.*;

public class Connect{


public static void main (String[] args) {
Connection conn = null;
try {
String userName = “root”;
String password = “localhost”;
String url = “jdbc:mysql://127.0.0.1:3306/mysql”;
Class.forName (“com.mysql.jdbc.Driver”).newInstance ();
conn = DriverManager.getConnection (url,userName, password);
System.out.println (“Da ket noi CSDL”);
}
catch (Exception e) {
System.err.println (“KHONG KET NOI DUOC”);
}
finally {
if (conn != null) {
try {
conn.close ();
System.out.println (“Dong ket noi”);
}
catch (Exception e) { /* bo qua loi luc dong csdl */ }
}
}
}
}

Trước hết bạn hãy biên dịch file này ra mã bytecode nhưng để chạy nó, bạn cần chú ý đảm bảo MySQL đang chạy trên
máy tính cá nhân của bạn. Nếu bạn đã cài đặt MySQL để nó chỉ chạy khi bạn gọi thì bạn hãy bật MySQL với câu lệnh
mysqld-nt –console hay nếu bạn đã cài chương trình quản trị cơ sở dữ liệu này dưới hình thức một dịch vụ thì bạn có
thể gõ net start mysql trong shell Run trong Windows.
Đoạn mã trên sử dụng cặp tên người sử dụng và mật khẩu là root/localhost với cơ sở dữ liệu là mysql và giao thức
TCP/IP để kết nối vào hệ cơ sở dữ liệu MySQL.

* Phân tích

Để kết nối và sử dụng một cơ sở dữ liệu từ Java applet, servlet hay ứng dụng Java thì chúng ta cần thực hiện qua 3
bước:
1. Đăng kí/Nạp (các) driver
2. Thiết lập kết nối đến cơ sở dữ liệu
3. Gửi câu lệnh SQL và xử lý kết quả thu về
Bước đầu tiên để tạo ra một kết nối giữa ứng dụng Java và một cơ sở dữ liệu là đăng kí một JDBC driver với máy ảo
Java (JVM) mà ứng dụng Java chạy trên đó. Với cơ chế kết nối truyền thống (sẽ nói trong phần cơ chế kết nối
DataSource, thảo luận sau), thì kết nối và tất cả các hình thức giao tiếp với cơ sở dữ liệu đều do đối tượng
DriverManager kiểm soát. Để thiết lập một đường kết nối, một JDBC driver thích hợp dùng cho cơ sở dữ liệu mục tiêu
phải được đăng kí với đối tượng DriverManager.
Đặc tả JDBC chỉ ra rằng các JDBC driver được xem là sẽ tự đăng kí với đối tượng DriverManager một cách tự động
khi chúng được nạp vào một JVM. Ví dụ đoạn mã sau sử dụng một bộ khởi tạo tĩnh để lần đầu tiên tạo ra một mẫu của
JDBC driver mang tên persistentjava và sau đó đăng kí nó với DriverManager.
static {
java.sql.DriverManager.registerDriver(new
com.persistentjava.JdbcDriver()) ;
}

Việc đăng kí một driver đơn giản chỉ là việc nạp lớp driver vào JVM. Chúng ta có thể làm điều đó bằng một số cách
khác nhau. Một trong những cách đó là thông qua
ClassLoader Class.forName(com.persistentjava.JdbcDriver) ;.
Một cách khác, không phổ biến lắm là dùng thuộc tính hệ thống jdbc.drivers. Chúng ta có thể dùng cách này theo 3
hình thức khác nhau:
Từ dòng lệnh:java -Djdb.drivers =com.persistentjava.JdbcDriver Connect
* Bên trong ứng dụng java:
System.setProperty(“jdbc.drivers”, “com.persistentjava.JdbcDriver”) ;

* Bằng cách thiết lập thuộc tính jdbc.drivers vào file thuộc tính hệ thống, nhưng nhìn chung là lại tùy thuộc vào hệ
thống Bằng phương pháp tách các driver bằng một dấu phẩy, chúng ta có thể đăng kí nhiều driver thông qua kĩ thuật
thuộc tính hệ thống ở trên. Một trong các lợi ích của kĩ thuật này là ở chỗ các driver có thể đưa vào và ra khỏi JVM mà
không cần thay đổi mã (hoặc chỉ cần thay đổi ở mức tối thiểu). Nếu đăng kí nhiều driver, thứ tự ưu tiên của chúng như
sau:
1)Các JDBC driver do thuộc tính jdbc.drivers đăng kí lúc khởi tạo JVM, và
2)Các JDBC driver được nạp động. Do thuộc tính jdbc. drivers chỉ được kiểm tra một lần vào lần gọi đầu tiên của
phương thức DriverManager(), nên quan trọng là bạn phải đảm bảo rằng tất cả các driver được đăng kí đúng trước khi
thiết lập kết nối.
Chú ý là có một số JVMs không tuân thủ đặc tả JVM và kết quả là các bộ khởi tạo tĩnh không phải lúc nào cũng làm
việc thống nhất như mong muốn. Điều này dẫn tới việc có nhiều cách đăng kí một JDBC driver:
Class.forName(”com.persistentjava. JdbcDriver”).newInstance() ;
Hoặc
DriverManager.registerDriver(new com.persistentjava.JdbcDriver()) ;
Hai cách trên thường hoạt động tốt trong tất cả các JVM nên bạn có thể sử dụng chúng một cách thoải mái. Nhưng
điều khác biệt là Class.forName() có thể chặn bắt một ClassNotFoundException, nên bạn cần bao mã đăng kí driver
vào một bộ xử lý lỗi ngoại lệ thích hợp.
Ví dụ:

try{
Class.forName (“com.mysql.jdbc.Driver”).newInstance ();
}
catch (Exception e){
System.err.println (“KHONG KET NOI DUOC”);
}

Các sách về MySQL khuyến cáo bạn nên dùng cách trên. Phương thức Class.forname() nhận một chuỗi làm đối số.
Trong trường hợp bạn dùng JDBC Driver của hãng MySQL AB cho MySQL bản 4.0 trở lên thì chuỗi đó là:
com.mysql.jdbc.Driver

Cần chú ý là bạn không cần phải tạo ra một mẫu của driver và đăng kí nó với DriverManager vì việc gọi
Class.forName() sẽ làm cho bạn điều đó một cách tự động.
Bước tiếp theo sau khi bạn đã đăng kí JDBC Driver là thực hiện kết nối. Toàn bộ công đoạn này được minh họa qua
dòng mã sau:
Connection conn = DriverManager.getConnection(url, “myLogin”, “myPassword”);

Phương thức getConnection() được gọi để thực hiện một kết nối thực sự đến phần mềm máy chủ cơ sở dữ liệu. Lớp
DriverManager tạo ra một đối tượng Connection khi bạn gọi phương thức getConnection(). Phương thức này nhận một
chuỗi URL làm đối số. Ví dụ, với MySQL thì chuỗi đó có thể là:
jdbc:mysql://127.0.0.1:3306/mysql hoặc jdbc:mysql://localhost/mydatabase
Chuỗi này tuân theo quy tắc sau đây: jdbc:<subprotocol>:<subname> trong đó:
<subprotocol> thường là driver hoặc cơ chế kết nối cơ sở dữ liệu
<subname> phụ thuộc vào <subprotocol>, có thể thay đổi theo các nhà cung cấp driver mà nó có thể là mysql, db2,
oracle hay odbc.
<subname> có thể chứa địa chỉ Internet cho các cơ sở dữ liệu ở xa. Phần này có thể gồm có tên host, mã số cổng, và
tên của hệ thống cơ sở dữ liệu.
Chú ý là trong một số sách khác, công thức trên còn trình bày dưới dạng: jdbc:sub-protocol:database locator tuy về
bản chất thì không có gì thay đổi.
Nếu một trong các driver mà bạn cung cấp nhận ra địa chỉ JDBC URL mà bạn cung cấp cho phương thức
DriverManager.getConnection, thì driver đó sẽ thiết lập đường kết nối đến MySQL xác định trong JDBC URL.
Lớp DriverManager sẽ quản lý tất cả các chi tiết của việc thiết lập đường kết nối bên dưới nên bạn không cần phải
quan tâm gì thêm.
Đường kết nối do phương thức DriverManager.getConnection trả về là một đường kết nối mở mà bạn có thể sử dụng
để thông qua các lệnh JDBC nhằm gửi các lệnh SQL tới MySQL. Ở đây chúng ta có đối tượng conn chứa kết nối mở
và chúng ta sẽ sử dụng chúng để thực hiện các ví dụ thao tác trên MySQL

Thực hiện các câu truy vấn

Để 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á rị của nó tự động tăng thêm mà không cần chúng ta rực tiếp can thiệp,
vì MySQL tự động gán các giá trị ố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 và 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ả

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ề:

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() và 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);

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:

String nameVal = rs.getString (“name”);


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

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() và
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());
}

Chúng ta tiếp tục đi sâu vô một tí xíu và làm quen với ResultSetMetaData, Statement và PreparedStatement.

Các thành phần JDBC


1.MetaData
Hẳn bạn đã nghe đến thuật ngữ này, có sách dịch là “siêu dữ liệu”, cái khác thì dịch là “dữ liệu của dữ liệu”, còn tôi thì chẳng dám dịch
ra Tiếng Việt, thành thử cứ giữ nguyên là metadata.
Phương thức getMetaData() của ResultSet sẽ trả về cho bạn một đối tượng của kiểu ResultSetMetaData, đối tượng này chứa thông
tin về ResultSet. Như bạn đã biết, bên trong ResultSet thực chất là một table được trả về từ câu lệnh truy vấn, metadata của ResultSet
sẽ là: số cột của table, tên của các cột, tên của table...Chúng ta hãy xem qua ví dụ:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;

public class MetaDataExample {


private static String driver = "org.gjt.mm.mysql.Driver";
private static String url = "jdbc:mysql://localhost:3306/test";
private static String username = "root";
private static String password = "";

public static void main(String[] args) {


try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);

String query = "SELECT * FROM books";


Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery(query);

// Lay ve resultset metadata


ResultSetMetaData metadata = rs.getMetaData();

System.out.print("Ten cua table:");


System.out.println(metadata.getTableName(1));

System.out.print("So cot trong table:");


System.out.println(metadata.getColumnCount());

System.out.print("Ten cua cot thu 1:");


System.out.println(metadata.getColumnName(1));

System.out.print("Cot thu nhat co phai la auto increment ko?:");


System.out.println(metadata.isAutoIncrement(1));

//System.out.println(metadata.getColumnType(1));
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
catch(SQLException sqle) {
sqle.printStackTrace();
}
}
}

Output:
Ten cua table:books
So cot trong table:3
Ten cua cot thu 1:isbn
Cot thu nhat co phai la auto increment ko?:true

2.Statement
Chúng ta cũng đã nói sơ về Statement trong bài trước. Statement là một interface, nó định nghĩa các phương thức để giúp bạn tương
tác với database. Phương thức createStatement() của đối tượng Connection sẽ trả về cho bạn một đối tượng mà implement Statement
interface này.
* Giới hạn số dòng trả về.
Thông thường khi bạn thực hiện câu lệnh truy vấn bạn sẽ không biết có bao nhiêu hàng trong ResultSet được trả về. Statement cho
phép bạn giới hạn số dòng thông qua phương thức setMaxRows(...), bạn cũng có thể lấy về số dòng tối đa bằng phương thức
getMaxRows(). Lưu ý là giá trị 0 có nghĩa là không giới hạn.
* Time-out cho câu lệnh truy vấn
Bạn có thể thiết lập time-out cho một câu lệnh truy vấn thông qua phương thức setQueryTimeout() và bạn cũng có thể lấy về giá trị của
time-out bằng phương thức getQueryTimeout(). Giá trị 0 có nghĩa là không giới hạn.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class StatementExample {


private static String driver = "org.gjt.mm.mysql.Driver";
private static String url = "jdbc:mysql://localhost:3306/test";
private static String username = "root";
private static String password = "";

public static void main(String[] args) {


try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);

Statement stm = conn.createStatement();

int mr = stm.getMaxRows();
System.out.println("Maximum Rows: " + (mr == 0 ? "No Limit" : mr + ""));

int timeout = stm.getQueryTimeout();


System.out.println("Time-out:" + (timeout == 0 ? "No Limit" : timeout + ""));

// String ins = "INSERT INTO books(title, publisher) VALUES('C# can ban', 'NXB Kim Dong')";
// stm.executeUpdate(ins);
//
// String update = "UPDATE books SET title='Lap trinh can ban' WHERE isbn=1";
// stm.executeUpdate(update);
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
catch(SQLException sqle) {
sqle.printStackTrace();
}
}
}

Output:
Maximum Rows:No Limit
Time-out:No Limit

* Làm việc với DDL và DML


DDL là viết tắt của data definition language, như CREATE TABLE hay DROP TABLE.
DML là viết tắt của data manipulation language, như INSERT, DELETE, UPDATE
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class StatementExample {


private static String driver = "org.gjt.mm.mysql.Driver";
private static String url = "jdbc:mysql://localhost:3306/test";
private static String username = "root";
private static String password = "";

public static void main(String[] args) {


try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);

Statement stm = conn.createStatement();

String ins = "INSERT INTO books(title, publisher) VALUES('C# can ban', 'NXB Kim Dong')";
stm.executeUpdate(ins);

String update = "UPDATE books SET title='Lap trinh can ban' WHERE isbn=1";
stm.executeUpdate(update);

String delete = "DELETE FROM books WHERE isbn=2";


stm.executeUpdate(delete);
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
catch(SQLException sqle) {
sqle.printStackTrace();
}
}
}

3.PreparedStatement
Quay lại với Statement một chút, khi bạn muốn thực hiện một câu lệnh SQL với Statement bạn phải truyền vào một String, chuỗi này là
câu lệnh mà bạn muốn thực hiện. Ví dụ với table books ở bài viết trước, bạn muốn thực hiện truy vấn chọn ra cuốn sách dựa vào isbn
của nó. Nếu sử dụng Statement, mỗi lần bạn phải truyền vào chuỗi như thế này:
SELECT * FROM books WHERE isbn = 1 (lần 1)
SELECT * FROM books WHERE isbn = 2 (lần 2)
SELECT * FROM books WHERE isbn = 3 (lần 3)
Ở bên dưới, mỗi lần thực hiện một câu lệnh thì database lại làm nhiệm vụ phân tích (parse) chuỗi truy vấn, sau đó compile nó sang
ngôn ngữ mà database có thể hiểu được. Công việc này làm đi làm lại nhiều lần sẽ ảnh hưởng tới hiệu suất của chương trình, đặc biệt
là với database có lượng dữ liệu lớn. Làm thế nào để câu lệnh SQL chỉ được phân tích và compile một lần, may mắn là chúng ta đã có
PreparedStatement. Lúc này câu lệnh của bạn chỉ còn lại thế này:
SELECT * FROM books WHERE isbn = ?

Ký tự “?” đây có ý nghĩa: dành trước một chỗ để sau này muốn thực hiện câu lệnh trên chỉ cần set giá trị cho isbn là được.
PreparedStatement pstm = connection.prepareStatement(“SELECT * FROM books WHERE isbn = ?”);

pstm.setInt(1, 1);
ResultSet rs = pstm.executeQuery();

Trước khi thực hiện câu lệnh SQL, bạn phải gán giá trị cho những chỗ có ký tự “?”. Thứ tự lần lượt của các ký tự “?” sẽ bắt đầu từ 1
PreparedStatement pstm = connection.prepareStatement(“SELECT * FROM books WHERE isbn = ? AND title = ?”);

pstm.setInt(1, 1);
pstm.setString(2, “Lap trinh Java”);
ResultSet rs = pstm.executeQuery();

PreparedStatement định nghĩa một tập hợp các phương thức setXXX() để bạn gán giá trị
setString()
setInt()
setBoolean()
setObject()
...

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class PreparedStatementExample {


private static String driver = "org.gjt.mm.mysql.Driver";
private static String url = "jdbc:mysql://localhost:3306/test";
private static String username = "root";
private static String password = "";

public static void main(String[] args) {


try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);
String ins = "INSERT INTO books(title, publisher) VALUES(?, ?)";
PreparedStatement pstm = conn.prepareStatement(ins);
pstm.setString(1, "Nghe thuat song");
pstm.setString(2, "NXB Tre");

pstm.executeUpdate();

String update = "UPDATE books SET title=? WHERE isbn=?";


PreparedStatement pstm1 = conn.prepareStatement(update);
pstm1.setString(1, "Title");
pstm1.setInt(2, 1);
pstm1.executeUpdate();
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
catch(SQLException sqle) {
sqle.printStackTrace();
}
}
}
4.Sử dụng đối tượng ResultSet

Đối tượng ResultSet trong lập trình JDBC rất quan trọng, nó lưu giữ các mẫu tin thỏa mãn 1 tính chất nào đó của
database, cho phép thực hiện các thao tác trên nó,…

ResultSet có được khi chúng ta thực thi 1 câu select SQL từ đối tượng xStatement. Sau khi có được đối tượng ResultSet
ta có thể đọc 1 mẫu tin bằngphương thức next(). Phương thức này trả về false khi không có mẫu tin để đọc (có thể là
cuối danh sách). Sau khi đọc 1 mẫu tin, ta có thể lấy dữ liệu từ mẫu tin hiện tại bằng cách dùng các phương thức
getXXX(”fieldName hoặc index”) trong đó XXX là kiểu dữ liệu của field cần đọc. Vì do kiểu của csdl và kiểu của java là
không giống nhau nên ta phải chú ý ánh xã từ kiểu của csdl sang kiểu của java tương ứng. Tuy nhiên trong 1 số trường
hợp, jdbc tự ép kiểu cho chúng ta theo nguyên tắc của việc chuyển kiểu.

Về tham số truyền vào phương thức getXXX, tôi nghĩ là chúng ta nên truyền vào tên field thay thì chỉ số index vô tri vô
giác. Lưu ý: trong lập trình JDBC, index luôn bắt đầu từ 1. Vi dụ :

void TestExecuteQuery() {
try {
con=ConnectDBFactory.CreateMsSqlServerConnection("BookMS");
Statement stmt =
con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
String sql1="select * from book";
ResultSet rs=stmt.executeQuery(sql1);
while(rs.next()) {
String ms=rs.getString("isbn");
String mk=rs.getString("bookTiitle");
System.out.println(ms+"\t"+mk);
}
rs.previous();
String ms=rs.getString("isbn");
System.out.println("Sau khi di lui");
System.out.println(ms);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

5.Xử lý theo khối (batch processing)


• Nhóm nhiều câu lệnh vào trong một khối (batch) . Sau đó thi hành cả một khối lệnh cùng một lúc .
• Thêm lệnh vào khối lệnh

- Method addBatch(String sql) (java.sql.Statement)


- Method addBatch() (java.sql.PreparedStatement )
• Thi hành khối lệnh
- Method executeBatch() (java.sql.Statement)

Ví dụ với việc xử lý theo khối (batch processing)


// Một kết nối tới một cơ sở dữ liệu nào đó
Connection connection = .... ;
Statement statement=connection.createStatement();
//Thêm một nhiệm vụ vào khối (DELETE )
statement.addBatch("DELETE FROM Account WHERE id=200");
// accounts là một mảng đối tượng Java .
for(int i=0;i<accounts.length;i++){
// Thêm một nhiệm vụ vào khối (UPDATE).
statement.addBatch("UPDATE Account "+
"SET balance="+accounts[i].getBalance()+
" WHERE id="+accounts[i].getId());
}
/** Thi hành cả khối nhiệm vụ.Trong đó int[] rows là mảng , mà mỗi phần tử là
số hàng bị tác động bởi từng lệnh (nhiệm vụ ) trong khối */
int[] rows=statement.executeBatch();

Ví dụ sự khác nhau giữa các bước sử dụng PreparedStatement và batch ?


String sql="UPDATE Account SET balance=? WHERE id=?";
// Chuẩn bị câu lệnh SQL được dịch sẵn .
PreparedStatement pstm=connection.preparedStatement(sql);
//accounts là một mảng đối tượng Java .
for(int i=0;i<accounts.length;i++){
pstm.setFloat(1,accounts[i].getBalance());
pstm.setInt(2,accounts[i].getId());
//Thêm nhiệm vụ này vào khối .
pstm.addBatch();
}
//Thi hành cả khối lệnh
pstm.executeBatch();

• Đoạn code trên đây là một ví dụ minh họa việc UPDATE đồng loạt các hàng trong bảng Account trong cơ sở dữ liệu với
Batch .Trước đây chúng ta đã làm việc tương tự này sử dụng PreparedStatement .Hãy nhìn vào bảng so sánh các bước
đã làm của 2 cách .
PreparedStatement
Sử lý theo khối (Batch processiong)

1) Tắt bỏ chế độ tự động commit .


2) Chuẩn bị đối tượng PreparedStatement .
3) Gán giá trị (Tại vị trí cho các dấu ? )
4) Thi hành (execute ) (Nhưng thay đổi chưa diễn ra trong
DataBase ).
5) Lặp lại bước 3,4 nhiều lần (phụ thuộc vào ứng dụng)
6) Gọi method commit() (Tạo thay đổi đồng loạt diễn ra
trong Database) .
1) Chuẩn bị đối tượng PreparedStatement .
2) Gán các giá trị (tại các vị trí dấu ?)
3) Thêm vào khối .
4) Lặp lại bước 2,3 nhiều lần phụ thuộc vào ứng dụng .
5) Thực thi khối lệnh .

6.CallableStatement

Phần này giới thiệu với các bạn cách chúng ta thao tác với các StoredProcedure trong java với đối tượng
CallableStatement.
CallableStatement là đối tượng kế thừa từ đối tượng PreparedStatement, như vậy có các tính chất của 1
PreparedStatement (tức có các tính chất của Statement(public interface CallableStatement extends PreparedStatement).
Chúng ta tạo 1 CallableStatement bằng cú pháp sau:

CallableStatement prepareCall(String sql) throws SQLException hoặc

CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException.

Sau đó chúng ta triệu gọi các stored procedure bằng các cú pháp sau:

Đối với procedure không có tham số: {call procedure_name}

Đối với procedure có tham số: {call procedure_name(?,?,…)}

Đối với procedure có giá trị trả về (function): {?=call procedure_name(?,?,…)}

Ví dụ: CallableStatement cstmt = con.prepareCall(”{call procedureName(?, ?)}”);

trong đó các dấu ? thay thế cho các tham số kiểu IN, OUT, INOUT.

Đối với tham số kiểu IN, ta sử dụng phương thức setXXX của đối tượng PrepareStatement để chuyển tham số vào. ví dụ:
cstmt.setString(1, “c%”);

Đối với tham số kiểu OUT, ta phải đăng ký đầu ra trước khi thực thi, ví dụ:
stmt.registerOutParameter(1, java.sql.Types.INTEGER);

Đối với tham số dạng INOUT, ta kết hợp gồm IN và OUT.

Cho dễ hiểu, tôi sẽ viết 1 ví dụ thực thi các kiểu StoredProcedure cho các bạn.

Trước hết ta tạo 1 database có tên QLSV trong đó có 1 bảng có tên Lophoc . SQL script cho database như sau:

CREATE TABLE [dbo].[Lophoc](


[classID] [nvarchar](15) COLLATE Vietnamese_CI_AS NOT NULL,
[className] [nvarchar](100) COLLATE Vietnamese_CI_AS NOT NULL,
[studentsNum] [int] NOT NULL,
[deptID] [nvarchar](15) COLLATE Vietnamese_CI_AS NULL,
CONSTRAINT [PK_tblClass] PRIMARY KEY CLUSTERED
(
[classID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

Sau đó chúng ta tạo các procedure để thử. Script sau đây sẽ tạo các stored procedure cho chúng ta:

–Tạo proc cho tham số IN


create proc exInparams
@clsID varchar(15)
AS
select *
from tblClass
where tblClass.classID like @clsID
GO
–Thực thi
exec exInparams ‘c%’
–********************************************
-Tạo proc cho tham số output
create proc exOutParams
@cls varchar(15),@num int OUTPUT
as
select @num=count(classID)
from tblClass
where classID like @cls
go
–Thực thi
declare @s int
exec exOutParams ‘c%’,@s OUTPUT
print @s
–********************************************
-Tạo proc cho tham số INOUT
create proc exINOUTParams
@num int OUT
as
select @num=count(classID)
from tblClass
where studentsNum>@num
go
select * from tblClass
–Thực thi
declare @s int
set @s=100
exec exINOUTParams @s OUTPUT
print @s
–********************************************
–Hàm Function
–Hàm lấy số SV của 1 lớp khi biết mã số lớp
create FUNCTION dbo.getNoStudents (@classID varchar(15))
RETURNS int
AS
BEGIN
Declare @c int
set @c=(select studentsNum from tblClass where classID = @classID)
return @c
END
go
–Thực thi
declare @x int
set @x=dbo.getNoStudents(’cdth4c’)
print(@x)
–********************************************

Bạn tạo DB rồi chạy các hàm để thử trên hệ quản trị CSDL, chẳng hạn MS SQLserver.

bạn tạo lớp sau để thử các loại:

import java.sql.*;

//thử tham số đầu vào


public class TestCallable {

private Connection con;

void TestINPro() throws Exception {


con = ConnectDBFactory.CreateMsSqlServerConnection("QLSV");
String sql = "{call exInparams(?)}";
CallableStatement cstmt = con.prepareCall(sql);
cstmt.setString(1, "c%");
ResultSet rs = cstmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("className"));
}
con.close();
}
//thử tham số đầu ra
void TestOUTPro() throws Exception {
con =
ConnectDBFactory.CreateMsSqlServerConnection("QLSV");
String sql = "{call exOutParams(?,?)}";
CallableStatement cstmt = con.prepareCall(sql);
cstmt.setString(1, "d%");
//đăng ký tham số đầu ra
cstmt.registerOutParameter(2, java.sql.Types.INTEGER);
cstmt.execute();
//Lấy giá trị trả về
int s = cstmt.getInt(2);
System.out.println(s);
con.close();
}
//Thử tham số vừa vào vừa ra
void TestINOUTPro() throws Exception {
con = ConnectDBFactory.CreateMsSqlServerConnection("QLSV");
String sql = "{call exINOUTParams(?)}";
CallableStatement cstmt = con.prepareCall(sql);
cstmt.setInt(1, 200);
//Đăng ký tham số đầu ra
cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
cstmt.execute();
//lấy giá trị trả về
int s = cstmt.getInt(1);
System.out.println(s);
con.close();
}
//Thử việc triệu gọi 1 function có giá trị trả về
public void TestFunctionCall() throws Exception {
ConnectDBFactory.CreateMsSqlServerConnection("QLSV");
CallableStatement cst = con.prepareCall("{?=call getNoStudents(?)}");
cst.registerOutParameter(1, java.sql.Types.INTEGER);
cst.setString(2, "CD");
cst.execute();
int ret = cst.getInt(1);
System.out.println("Số sinh viên: " + ret);
}
}

7.Transaction
Khái niệm về một giao dịch (transaction) là một khái niệm quan trọng , với các lập trình viên thì việc đối mặt với xử lý giao
dịch là tương đối thường xuyên . Một trong các ví dụ kinh điển về giao dịch có thể đưa ra là ứng dụng về chuyển khoản
giữa 2 công ty A và B .Công ty A muốn chuyển một khoản tiền X đến cho công ty B như vậy trong logic ứng dụng sẽ phải
xẩy ra 2 công đoạn một là trừ số tiền X ra khỏi tài khoản của A và hai là thêm vào tài khoản của B số tiền X . Điều gì sẽ
xẩy ra khi ứng dụng viết có sai sót số tiền của A bị trừ đi nhưng bước tiếp theo là thêm khoản tiền vào cho B không thực
hiện . Transaction có thể coi là một nhóm công việc không thể chia cắt , chỉ một công việc trong nhóm không thành công
thì giao dịch gọi là không thành công . Trong tình huống trên khi sai sót xẩy ra ứng dụng phải đưa tình trạng trở về như
trước khi thực hiện giao dịch (roll back) .

Transaction
• Một trong các lợi ích chính sử dụng PreparedStatement là thi hành các câu lệnh sql theo kiểu giao dịch.
• Sẽ là tốn thời gian khi mỗi câu lệnh chương trình hoàn tất luôn vào trong cơ sở dữ liệu.
• Bằng cách sét đặt chế độ tự động hoàn tất(commit) là false, lập trình viên có thể update cơ sở dữ liệu và hoàn tất
trọn vẹn giao dịch.
• Cũng vậy , mỗi câu lệnh sql phụ thuộc vào các câu lệnh khác, toàn bộ giao dịch có thể bị đưa trở lại trạng thái
ban đầu như trước khi giao dịch (roll back) và người dùng sẽ được thông báo về điều này.

Ví dụ cho Transactions:
Connection connection = null;
try {
connection = DriverManager.getConnection(
"jdbc:oracle:thin:@machinename:15 21:dbname",
"username","password");
connection.setAutoCommit(false);
PreparedStatement updateQty =
connection.prepareStatement("UPDATE STORE_SALES SET QTY = ?"
+" WHERE ITEM_CODE = ? ");

-Để bắt đầu một giao dịch (transaction), bạn tạo một đối tượng PreparedStatement như thông thường .
-Với đối tượng Connection bạn tắt bỏ đi chế độ tự động hoàn tất vào database (commit) connection.setAutoCommit(false)
. Vì mặc định là chế độ commit được sét đặt là true .

Ví dụ cho Transaction (tiếp theo)


// Mảng 2 chiều các giá trị
int[][] arrValueToUpdate =
{ {123, 500},
{124, 250},
{125, 10},
{126, 350} };
int iRecordsUpdate = 0;
for ( int items=0 ; items < arrValueToUpdate.length ; items++) {
int itemCode = arrValueToUpdate[items][0];
int qty = arrValueToUpdate[items][1];

Ở đây chúng ta tạo ra một mảng 2 chiều và sẽ dùng mảng này như một mảng dữ liệu mà chúng ta sẽ phải upload đồng
loạt chúng vào trong cơ sở dữ liệu cùng một lần .

Transaction Example cont.


updateQty.setInt(1,qty);
updateQty.setInt(2,itemCode);
iRecordsUpdate += updateQty.executeUpdate();
}
connection.commit();
System.out.println(iRecordsUpdate + " record(s) have been updated");
} catch(SQLException sqle) {//Nếu có gì đó sai xót sẩy ra khi chạy
System.out.println("" + sqle);

-Sau mỗi lần sét đặt giá trị cho đối tượng PreparedStatement ứng với các vị trí dấu ? , chúng ta gọi method
executeUpdate() nhưng nó thực sự chưa tạo ra thay đổi trong cơ sở dữ liệu
-Sau vòng lặp chúng ta gọi method commit() để tạo ra sự thay đổi đồng loạt trong cơ sở dữ liệu .Nếu bạn không gọi
method này sẽ không có gì thay đổi trong cơ sở dữ liệu hết .
try {
/* Khi có sai sót xẩy ra gọi method roolback() .Để khôi phục lại
trạng thái của cơ sở dữ liệu như trước khi gọi các lệnh update trên */
connection.rollback();
}catch(SQLException sqleRollback) {
System.out.println("" + sqleRollback);
}
} finally {
try {
connection.close();
}catch(SQLException sqleClose) {
System.out.println("" + sqleClose);
}

-Toàn bộ quá trình sử lý được đặt trong khối try/catch . Bất kỳ một sai sót nào trong quá trình chạy update vào cơ sở dữ
liệu sẽ được tóm bởi SQLException , bạn sẽ ra lệnh cho chương trình khôi phục lại trạng thái như trước khi gọi các câu
lệnh update .
-Cũng vậy trong vòng khối finally bạn đóng kết nối với cơ sở dữ liệu lại .

You might also like