Tại sao giao dịch là cần thiết

Rất thường xuyên, khi làm việc với cơ sở dữ liệu, một tình huống phát sinh khi bạn cần thực hiện nhiều hành động khác nhau, nhưng chúng chỉ có ý nghĩa khi kết hợp với nhau.

Ví dụ: chúng tôi đang viết phần mềm ngân hàng sẽ thực hiện ba việc:

  • Rút tiền từ tài khoản của khách hàng
  • Thêm tiền vào tài khoản của người nhận
  • Ghi lại dữ liệu đăng bài trong “nhật ký đăng bài”

Nếu xảy ra lỗi trong quá trình thực hiện bất kỳ hành động nào trong số này, thì hai hành động còn lại cũng phải bị hủy. Không thể viết tắt tiền từ khách hàng và không thêm nó cho người nhận? Chà, hoặc thêm vào người nhận, nhưng không viết tắt từ khách hàng?

Vì vậy, một nhóm hợp lý các hành động khác nhau thành một được gọi là giao dịch . Nói cách khác, giao dịch là một nhóm các hành động chỉ được thực hiện cùng nhau . Nếu bất kỳ hành động nào không thành công hoặc được thực hiện có lỗi, thì tất cả các hành động khác phải bị hủy.

Một giao dịch thường có ba trạng thái:

  • trạng thái ban đầu - trạng thái của hệ thống trước khi thực hiện một nhóm hành động
  • trạng thái thành công - trạng thái sau khi hoàn thành nhóm hành động
  • trạng thái thất bại - đã xảy ra sự cố

Trong trường hợp này, thường có ba lệnh:

  • bắt đầu/bắt đầu - được thực hiện trước khi bắt đầu nhóm hành động hợp lý
  • cam kết - được thực hiện sau nhóm hành động giao dịch
  • rollback - bắt đầu quá trình đưa hệ thống từ trạng thái bị lỗi về trạng thái ban đầu

Nó hoạt động như thế này.

Trước tiên, bạn cần mở một giao dịch - gọi phương thức start() hoặc start() . Việc gọi phương thức này cho biết trạng thái của hệ thống mà chúng tôi sẽ cố gắng quay lại nếu có sự cố xảy ra.

Sau đó, tất cả các hành động được thực hiện, được kết hợp thành một nhóm logic - một giao dịch.

Sau đó, phương thức commit() được gọi là . Cuộc gọi của nó đánh dấu sự kết thúc của một nhóm hành động hợp lý và cũng thường bắt đầu quá trình đưa những hành động này vào thực tế.

Nhớ lại cách chúng tôi đã viết một cái gì đó trong FileWriter: đầu tiên, mọi thứ chúng tôi đã viết được lưu trữ trong bộ nhớ và sau đó khi phương thức flush() được gọi , tất cả dữ liệu từ bộ đệm trong bộ nhớ sẽ được ghi vào đĩa. flush() này là cam kết giao dịch.

Chà, nếu xảy ra lỗi trong quá trình thực hiện giao dịch, thì bạn cần bắt đầu quá trình quay lại trạng thái bắt đầu. Quá trình này được gọi là rollback() và phương thức cùng tên thường chịu trách nhiệm về nó.

Nói một cách đại khái, có 2 cách để hoàn thành giao dịch:

  • CAM KẾT - chúng tôi xác nhận tất cả các thay đổi đã thực hiện
  • ROLLBACK - quay lại tất cả các thay đổi đã thực hiện

Giao dịch trong JDBC

Hầu như mọi DBMS đều có thể hoạt động với các giao dịch. Vì vậy, JDBC cũng có hỗ trợ cho trường hợp này. Mọi thứ được thực hiện rất đơn giản.

Đầu tiên, mỗi lệnh gọi phương thức exec() của đối tượng Statement được thực thi trong một giao dịch riêng biệt. Để làm điều này, Kết nối có tham số AutoCommit . Nếu nó được đặt thành true , thì commit() sẽ được gọi sau mỗi lần gọi phương thức exec() .

Thứ hai, nếu bạn muốn thực hiện nhiều lệnh trong một giao dịch, thì bạn có thể thực hiện như sau:

  • vô hiệu hóa AutoCommit
  • gọi lệnh của chúng tôi
  • gọi phương thức commit() một cách rõ ràng

Nó trông rất đơn giản:

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");

connection.commit();

Nếu xảy ra lỗi trên máy chủ trong khi phương thức commit() đang chạy thì máy chủ SQL sẽ hủy bỏ cả ba hành động.

Nhưng có những tình huống khi lỗi vẫn xảy ra ở phía máy khách và chúng tôi chưa bao giờ gọi phương thức commit() :

connection.setAutoCommit(false);

Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");

connection.commit();

Nếu xảy ra lỗi trong quá trình thực hiện một execUpdate() thì phương thức commit() sẽ không được gọi. Để khôi phục tất cả các hành động đã thực hiện, bạn cần gọi phương thức rollback() . Nó thường trông như thế này:

try{
  	connection.setAutoCommit(false);

  	Statement statement = connection.createStatement();
  	int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");

	  connection.commit();
 }
 catch (Exception e) {
   connection.rollback();
}

Điểm lưu trữ

Với sự ra đời của JDBC 3.0, nó có thể hoạt động hiệu quả hơn với việc khôi phục giao dịch. Bây giờ bạn có thể thiết lập các điểm lưu - save point, và khi bạn gọi thao tác rollback() , hãy quay lại một điểm lưu cụ thể.

Để lưu, bạn cần tạo một điểm lưu trữ, điều này được thực hiện bằng lệnh:

Savepoint save = connection.setSavepoint();

Hoàn nguyên về điểm lưu trữ được thực hiện bằng lệnh:

connection.rollback(save);

Hãy thử thêm một điểm lưu trữ trước lệnh có vấn đề của chúng tôi:

try{
  	connection.setAutoCommit(false);

  	Statement statement = connection.createStatement();
  	int rowsCount1 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");
  	int rowsCount2 = statement.executeUpdate("UPDATE  employee SET salary = salary+1000");

  	Savepoint save = connection.setSavepoint();
 	 try{
      	int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");
 	 }
 	 catch (Exception e) {
    	   connection.rollback(save);
 	 }

	  connection.commit();
 }
 catch (Exception e) {
   connection.rollback();
}

Chúng tôi đã tổ chức các giao dịch lồng nhau bằng cách thêm một điểm lưu trước khi gọi phương thức có vấn đề và quay lại trạng thái đã lưu bằng cách gọi phương thức khôi phục (lưu) .

Vâng, nó rất giống với lưu/tải trong trò chơi.