Khuôn khổ không có cách nào để biết liệu bạn đã bắt đầu một giao dịch hay chưa. Bạn thậm chí có thể sử dụng truy vấn $db->query('START TRANSACTION')
mà khung công tác sẽ không biết vì nó không phân tích cú pháp các câu lệnh SQL mà bạn thực thi.
Vấn đề là ứng dụng có trách nhiệm theo dõi xem bạn đã bắt đầu giao dịch hay chưa. Đó không phải là thứ mà khuôn khổ có thể làm.
Tôi biết một số khuôn khổ cố gắng làm điều đó và thực hiện những việc khó hiểu như đếm số lần bạn đã bắt đầu một giao dịch, chỉ giải quyết nó khi bạn đã thực hiện xong cam kết hoặc quay lại một số lần phù hợp. Nhưng điều này hoàn toàn không có thật vì không một hàm nào của bạn có thể biết liệu commit hay rollback có thực sự thực hiện hay không, hay chúng nằm trong một lớp lồng ghép khác.
(Bạn có thể cho biết tôi đã có cuộc thảo luận này vài lần không? :-)
Cập nhật 1: Propel là một thư viện truy cập cơ sở dữ liệu PHP hỗ trợ khái niệm "giao dịch bên trong" không cam kết khi bạn yêu cầu nó. Bắt đầu một giao dịch chỉ làm tăng bộ đếm, và cam kết / khôi phục làm giảm bộ đếm. Dưới đây là đoạn trích từ chuỗi danh sách gửi thư, nơi tôi mô tả một số trường hợp không thành công.
Cập nhật 2: Doctrine DBAL cũng có tính năng này. Họ gọi nó là tổ chức giao dịch.
Dù muốn hay không, các giao dịch là "toàn cầu" và chúng không tuân theo tính năng đóng gói hướng đối tượng.
Tình huống sự cố số 1
Tôi gọi commit()
, những thay đổi của tôi có được cam kết không? Nếu tôi đang chạy bên trong một "giao dịch bên trong" thì không phải vậy. Mã quản lý giao dịch bên ngoài có thể chọn khôi phục và các thay đổi của tôi sẽ bị hủy mà tôi không biết hoặc không kiểm soát được.
Ví dụ:
- Mô hình A:bắt đầu giao dịch
- Mô hình A:thực hiện một số thay đổi
- Mô hình B:bắt đầu giao dịch (không sử dụng im lặng)
- Mô hình B:thực hiện một số thay đổi
- Mô hình B:cam kết (không sử dụng im lặng)
- Mô hình A:khôi phục (loại bỏ cả các thay đổi của mô hình A và các thay đổi của mô hình B)
- Mô hình B:WTF !? Điều gì đã xảy ra với những thay đổi của tôi?
Tình huống sự cố số 2
Một giao dịch bên trong quay trở lại, nó có thể loại bỏ các thay đổi hợp pháp được thực hiện bởi một giao dịch bên ngoài. Khi quyền kiểm soát được trả lại cho mã bên ngoài, nó tin rằng giao dịch của nó vẫn đang hoạt động và có sẵn để được cam kết. Với bản vá của bạn, họ có thể gọi commit()
và vì transDepth bây giờ là 0, nó sẽ âm thầm đặt $transDepth
thành -1 và trả về true, sau khi không cam kết bất kỳ điều gì.
Tình huống sự cố số 3
Nếu tôi gọi commit()
hoặc rollback()
khi không có giao dịch nào hoạt động, nó sẽ đặt $transDepth
đến 1. beginTransaction()
tiếp theo tăng mức độ lên 0, có nghĩa là giao dịch không thể được khôi phục hoặc cam kết. Các lệnh gọi tiếp theo tới commit()
sẽ chỉ giảm giao dịch xuống -1 hoặc hơn nữa, và bạn sẽ không bao giờ có thể cam kết cho đến khi bạn thực hiện một beginTransaction()
thừa khác để tăng cấp độ một lần nữa.
Về cơ bản, cố gắng quản lý các giao dịch theo logic ứng dụng mà không cho phép cơ sở dữ liệu thực hiện việc ghi sổ là một ý tưởng diệt vong. Nếu bạn có yêu cầu đối với hai mô hình để sử dụng kiểm soát giao dịch rõ ràng trong một yêu cầu ứng dụng, thì bạn phải mở hai kết nối DB, một kết nối cho mỗi mô hình. Sau đó, mỗi mô hình có thể có giao dịch hoạt động của riêng mình, giao dịch này có thể được cam kết hoặc khôi phục độc lập với nhau.