Khi chúng tôi thực hiện một thủ tục được lưu trữ trong JDBC, chúng tôi nhận được một loạt các "kết quả" bằng không hoặc nhiều hơn. Sau đó, chúng tôi có thể xử lý các "kết quả" đó một cách tuần tự bằng cách gọi CallableStatement#getMoreResults()
. Mỗi "kết quả" có thể chứa
- không hoặc nhiều hàng dữ liệu mà chúng tôi có thể truy xuất bằng
ResultSet
đối tượng, - số lượng cập nhật cho một câu lệnh DML (INSERT, UPDATE, DELETE) mà chúng tôi có thể truy xuất bằng
CallableStatement#getUpdateCount()
hoặc - lỗi ném SQLServerException.
Đối với "Vấn đề 1", vấn đề thường là quy trình được lưu trữ không bắt đầu bằng SET NOCOUNT ON;
và thực hiện một câu lệnh DML trước khi thực hiện một SELECT để tạo ra một tập kết quả. Số lượng cập nhật cho DML được trả về dưới dạng "kết quả" đầu tiên và các hàng dữ liệu "bị kẹt sau nó" cho đến khi chúng tôi gọi getMoreResults
.
"Vấn đề 2" về cơ bản là cùng một vấn đề. Thủ tục được lưu trữ tạo ra một "kết quả" (thường là CHỌN, hoặc có thể là số lần cập nhật) trước khi lỗi xảy ra. Lỗi được trả về trong một "kết quả" tiếp theo và không gây ra ngoại lệ cho đến khi chúng tôi "truy xuất" nó bằng cách sử dụng getMoreResults
.
Trong nhiều trường hợp, vấn đề có thể tránh được bằng cách chỉ cần thêm SET NOCOUNT ON;
là câu lệnh thực thi đầu tiên trong thủ tục được lưu trữ. Tuy nhiên, không phải lúc nào cũng có thể thay đổi quy trình đã lưu trữ và thực tế là để có được mọi thứ trở lại từ quy trình đã lưu trữ, chúng ta cần tiếp tục gọi getMoreResults
cho đến khi, như Javadoc nói:
There are no more results when the following is true:
// stmt is a Statement object
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
Điều đó nghe có vẻ đơn giản nhưng như thường lệ, "ma quỷ ở trong chi tiết", như minh họa bằng ví dụ sau. Đối với thủ tục được lưu trữ trên SQL Server ...
ALTER PROCEDURE dbo.TroublesomeSP AS
BEGIN
-- note: no `SET NOCOUNT ON;`
DECLARE @tbl TABLE (id VARCHAR(3) PRIMARY KEY);
DROP TABLE NonExistent;
INSERT INTO @tbl (id) VALUES ('001');
SELECT id FROM @tbl;
INSERT INTO @tbl (id) VALUES ('001'); -- duplicate key error
SELECT 1/0; -- error _inside_ ResultSet
INSERT INTO @tbl (id) VALUES ('101');
INSERT INTO @tbl (id) VALUES ('201'),('202');
SELECT id FROM @tbl;
END
... mã Java sau sẽ trả về mọi thứ ...
try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) {
boolean resultSetAvailable = false;
int numberOfResultsProcessed = 0;
try {
resultSetAvailable = cs.execute();
} catch (SQLServerException sse) {
System.out.printf("Exception thrown on execute: %s%n%n", sse.getMessage());
numberOfResultsProcessed++;
}
int updateCount = -2; // initialize to impossible(?) value
while (true) {
boolean exceptionOccurred = true;
do {
try {
if (numberOfResultsProcessed > 0) {
resultSetAvailable = cs.getMoreResults();
}
exceptionOccurred = false;
updateCount = cs.getUpdateCount();
} catch (SQLServerException sse) {
System.out.printf("Current result is an exception: %s%n%n", sse.getMessage());
}
numberOfResultsProcessed++;
} while (exceptionOccurred);
if ((!resultSetAvailable) && (updateCount == -1)) {
break; // we're done
}
if (resultSetAvailable) {
System.out.println("Current result is a ResultSet:");
try (ResultSet rs = cs.getResultSet()) {
try {
while (rs.next()) {
System.out.println(rs.getString(1));
}
} catch (SQLServerException sse) {
System.out.printf("Exception while processing ResultSet: %s%n", sse.getMessage());
}
}
} else {
System.out.printf("Current result is an update count: %d %s affected%n",
updateCount,
updateCount == 1 ? "row was" : "rows were");
}
System.out.println();
}
System.out.println("[end of results]");
}
... tạo ra đầu ra bảng điều khiển sau:
Exception thrown on execute: Cannot drop the table 'NonExistent', because it does not exist or you do not have permission.
Current result is an update count: 1 row was affected
Current result is a ResultSet:
001
Current result is an exception: Violation of PRIMARY KEY constraint 'PK__#314D4EA__3213E83F3335971A'. Cannot insert duplicate key in object '[email protected]'. The duplicate key value is (001).
Current result is a ResultSet:
Exception while processing ResultSet: Divide by zero error encountered.
Current result is an update count: 1 row was affected
Current result is an update count: 2 rows were affected
Current result is a ResultSet:
001
101
201
202
[end of results]