/

14 tháng 7, 2013

Quản lý giao dịch Transcation (Phần 2)

Giao dịch lồng nhau


Các giao dịch có thể lồng vào nhau, bạn có thể bắt đầu một giao dịch bên trong một giao dịch khác và kết thúc hoặc hủy bỏ giao dịch gần đây.

Trình quản lý giao dịch duy trì các giao dịch trong một khối, trong đó những giao dịch gần nhất nằm trên đỉnh của khối. Khi bắt đầu một giao dịch mới sử dụng AcTransactionManager::startTransaction(), thì giao dịch đó được thêm vào tại đỉnh của khối và con trỏ đến nó được trả về (một thể hiện của AcTransaction). Khi một ai đó gọi AcTransactionManager::endTransaction() hoặc AcTransactionManager::abortTransaction(), thì giao dịch trên cùng của khối sẽ kết thúc hoặc bị hủy bỏ. 


Khi các con trỏ đối tượng được lấy nhờ vào mã ID, chúng luôn luôn liên hệ với giao dịch gần nhất. Bạn có thể lấy được giao dịch gần đây bằng cách sử dụng AcTransactionManager::topTransaction(), sau đó là AcTransaction::getObject() hoặc AcTransactionManager::getObject() để lấy được con trỏ đến một đối tượng. Trình quản lý giao dịch tự động liên kết với con trỏ đối tượng được lấy với giao dịch gần đây. Bạn có thể dùng AcTransaction::getObject() chỉ với giao dịch gần đây nhất

Khi các giao dịch bắt đầu lồng vào nhau, con trỏ đối tượng được lấy trong giao dịch ngoài cùng cũng dùng được cho các thao tác ở giao dịch trong nhất. Nếu giao dịch gần đây bị hủy bỏ, mọi quá trình hoàn thành trên tất cả các đối tượng (kết hợp với hoặc giao dịch này, hoặc giao dịch bên ngoài) từ lúc bắt đầu của giao dịch gần đây, được hủy bỏ và đối tượng quay trở lại trạng thái ban đầu trước khi giao dịch. Con trỏ đối tượng lấy được trong giao dịch gần đây sẽ mất hiệu lực khi nó bị hủy bỏ.

Nếu giao dịch trong cùng kết thúc thành công bằng cách gọi hàm AcTransactionManager::endTransaction(), các đối tượng có con trỏ lấy được trong giao dịch này sẽ liên kết với giao dịch bên ngoài và sử dụng được cho các quá trình tiếp theo. Quá trình như thế cứ tiếp diễn cho đến khi giao dịch ngoài cùng (đầu tiên) kết thúc, cùng lúc là mọi thay đổi trên các đối tượng sẽ được thực hiện đồng thời. Nếu giao dịch ngoài cùng bị hủy bỏ, mọi thao tác trên tất cả các giao dịch bên trong và chính nó cũng bị hủy bỏ theo.

Tránh giao dịch đệ quy (Recursive Transactions)


Cơ chế quản lý giao dịch trong AutoCAD dựa trên thực tế là các giao dịch không đệ quy. Khi một giao dịch kết thúc (hoặc hủy bỏ), bạn không cần cố gắng để khởi động và kết thúc (hoặc hủy bỏ) một giao dịch khác. Nếu làm như thế, có thể dẫn đến kết quả không lường trước và thiết ổn định. 

Để tránh gây ra vấn đề này, trước khi thực hiện một giao dịch, kiểm tra xem có an toàn để thực hiện hay không. Để làm điều này, thực thi một reactor giao dịch và đặt cờ flag để quyết định khi nào AutoCAD đang ở lưng chừng một quá trình kết thúc (hoặc hủy bỏ) giao dịch. Các phần khác của mã nguồn làm việc với giao dịch nên kiểm tra flag này trước khi tiến hành một giao dịch mới.

Bởi rất nhiều quá trình trong AutoCAD sử dụng quản lý giao dịch, và các quá trình này kích hoạt rất nhiều thông báo khác nhau, cho nên việc sử dụng mã thông báo để kiểm tra xem có an toàn khi thực thiện một giao dịch hay không là thật sự quan trọng.


Biên Giao dịch


Bởi vì chính bạn (không phải hệ thống) chịu trách nhiệm bắt đầu, kết thúc hoặc hủy bỏ giao dịch, nên việc ý thức về biên giao dịch là rất quan trọng. Một biên giao dịch là khoảng thời gian giữa lúc bắt đầu và kết thúc (hoặc hủy bỏ) một giao dịch. Nếu được, bạn hãy giới hạn biên giao dịch trong khoảng nhỏ nhất có thể. Ví dụ như, nếu bạn bắt đầu một giao dịch trong một hàm, hãy thử kết thúc giao dịch trước khi trả về từ hàm đó, bởi bạn có thể không có kiến thức về giao dịch bên ngoài hàm. Bạn không cần thực hiện quy tắc này nếu muốn duy trì một vài kiểu của trình quản lý toàn cầu cho các hoạt động giao dịch, nhưng bạn vẫn cần phải chịu trách nhiệm đối với việc hủy bỏ hay kết thúc giao dịch mà bạn đã khởi động trước đó.

Nhiều ứng dụng có thể sử dụng quản lý giao dịch cho công việc của chúng, và các thao tác trên các đối tượng  sẽ được thực hiện khi kết thúc giao dịch ngoài cùng. Vì thế, hãy kéo dài biên các giao dịch của bạn tối đa có thể trong biên của lệnh AutoCAD®. Khi một lệnh kết thúc, không nên có bất cứ một giao dịch đang hoạt động. Nếu còn giao dịch hoạt động (khối giao dịch không rỗng) khi một  lệnh kết thúc, AutoCAD sẽ hủy bỏ chúng. Có một ngoại lệ, giao dịch vẫn có thể hoạt động khi kết thúc lệnh và AutoCAD trả về dòng lệnh nhắc Command. Nói chung, ý tưởng tốt hơn cả là bắt đầu một giao dịch khi một trong số các hàm của bạn được khởi động như là một phần của lệnh đã đăng ký và kết thúc giao dịch khi bạn trả về từ hàm này. Bạn có thể khái quát nó vào tất cả các lệnh trong AutoCAD bằng cách sử dụng thông báo AcEditorReactor::commandWillStart()AcEditorReactor::commandEnded(), nhưng vẫn có những lệnh nhất định không nên sử dụng trong giao dịch. Các lệnh sau đây không nên thực hiện trong giao dịch:
  • ARX
  • DXFIN
  • INSERT
  • NEW
  • OPEN
  • PURGE
  • QUIT
  • RECOVER
  • REDO
  • SAVE
  • SCRIPT
  • U
  • UNDO
  • XREF

Lấy con trỏ đến đối tượng trong một giao dịch


Cả AcTransactionManager::getObject() lẫn AcTransaction::getObject() đều có thể được dùng để lấy con trỏ đối tượng từ ID của nó. Con trỏ lấy được sẽ làm việc với giao dịch gần nhất. Việc thử lấy con trỏ sử dụng các kết quả giao dịch khác sẽ gây ra lỗi. Ngoài ra, con trỏ lấy được cũng chỉ hợp lệ cho đến khi giao dịch mà chúng làm việc, hoặc một giao dịch bên ngoài, bị hủy bỏ. Khi giao dịch ngoài cùng kết thúc, các thay đổi trên các con trỏ hợp lệ sẽ được thực hiện.

Cả hai hàm getObject() đều lấy một tham số là AcDb::OpenMode và bạn có thể lấy con trỏ đối tượng để đọc, ghi, hoặc notify (thông báo). Mọi yêu cầu đều thành công, ngoài trừ trường hợp: nếu đối tượng đang thông báo và yêu cầu lấy con trỏ là để ghi (nhằm mục đích chỉnh sửa), lỗi (eWasNotifying) sẽ trả về. Không nên chỉnh sửa một đối tượng khi nó đang ở trong trạng thái thông báo đến các đối tượng khác.

Nếu bạn sử dụng getObject() để lấy con trỏ đối tượng, bạn không bao giờ được gọi phương thức close() trên con trỏ đó. Việc gọi close() chỉ hợp lệ nếu bạn lấy con trỏ bằng phương thức acdbOpenObject() hoặc đối tượng mới được tạo ra. Để có thêm thông tin về việc sử dụng phương thức close(), xem thêm các chuyên mục nhỏ dưới đây, Đối tượng mới tạo và giao dịch và Hoà trộn giữa mô hình giao dịch với cơ chế Mở và Đóng.

Đối tượng mới tạo và Giao dịch


Có hai cách để làm thực hiện việc tạo mới đối tượng trong một công việc quản lý giao dịch.

Phương pháp được khuyến nghị là sử dụng close(), đóng đối tượng sau khi thêm nó vào trong Cơ sở dữ liệu hoặc khoang chứa container thích hợp và lưu lại ID được trả về. Ngay sau khi đóng đối tượng, bạn đã có thể sử dụng hàm getObject() để lấy một con trỏ mới để thao tác tiếp theo. Thậm chí nếu bạn gọi phương thức close() trên đối tượng sau khi thêm nó vào trong CSDL, việc tạo ra nó sẽ bị hoàn tác nếu giao dịch bên ngoài bị hủy bỏ. Xem thêm Hoà trộn giữa mô hình giao dịch với cơ chế Mở và Đóng.

Phương pháp thứ hai là thêm đối tượng mới được tạo ra trong bộ nhớ vào CSDL hoặc khoang chứa thích hợp. Sau đó đưa nó vào trong giao dịch gần nhất bằng phương thức AcTransactionManager::addNewlyCreatedDBRObject() hoặc AcTransaction::addNewlyCreatedDBRObject. Bây giờ thì nó đã có thể làm việc với giao dịch. Mọi thay đổi trên đối tượng, thậm chí là việc tạo ra nó cũng sẽ được quyết định bởi giao dịch thành công hay hủy bỏ.

Nguyên tắc thời gian hẹn trước (Commit-Time) 


Khi giao dịch ngoài cùng kết thúc, trình quản lý giao dịch sẽ gọi hàm thông báo endCalledOnOutermostTransaction() (Xem trong Reactor giao dịch) và bắt đầu quá trình thực hiện các thay đổi hẹn trước. Mỗi đối tượng được thực hiện một cách riêng lẻ, cái này sau cái khác, cho đến khi tất cả các thay đổi hẹn trước được thực hiện. Trong suốt quá trình này, bạn không nên thay đổi bất cứ đối tượng nào đang tham gia quá trình, và cũng không nên bắt đầu một giao dịch mới. Nếu không, AutoCAD sẽ hủy bỏ với thông báo lỗi eInProcessOfCommitting

Bạn có thể chỉnh sửa từng đối tượng riêng lẻ sau khi quá trình hẹn trước được thực hiện xong, nhưng không nên lưu lại ID của đối tượng bạn muốn chỉnh sửa và đợi cho đến khi nhận được thông báo transactionEnded() báo hiệu kết thúc giao dịch, rồi mới thực hiện các chỉnh sửa. 

Undo và giao dịch


Mô hình giao dịch sử dụng cơ chế undo của AutoCAD và phương thức AcDbObject::cancel() khi thực thi AcTransactionManager::abortTransaction(). Nó yêu cầu bạn không được gộp bất kì quá trình có sử dụng cơ chế undo dưới lệnh của AutoCAD trong giao dịch. Nó sẽ gây nhầm lẫn cho  AcDbTransactionManager::abortTransaction() và tạo ra kết quả không mong muốn. Ví dụ của quá trình sử dụng cơ chế undo dưới lệnh là các lệnh PEDIT và SPLINEDIT.

Hoà trộn giữa mô hình giao dịch với cơ chế Mở và Đóng 

 
Mô hình giao dịch tồn tại song song với cơ chế đóng và mở đối tượng như đã nói ở chương 5, Đối tượng Cơ sở dữ liệu. Tuy nhiên nếu sử dụng mô hình giao dịch, khuyến cáo bạn không nên sử dụng lẫn lộn nó với cơ chế đóng mở. Ví dụ, nếu bạn lấy con trỏ tới đối tượng sử dụng AcTransaction::getObject(), bạn không nên gọi phương thức close() trên con trỏ của đối tượng nếu không muốn gặp phải những lỗi không mong đợi làm AutoCAD "chết bất đắc kỳ tử". Tuy nhiên, bạn vẫn có thể mở và đóng các đối tượng riêng rẽ khi các giao dịch vẫn đang hoạt động. Bạn cũng có thể khởi tạo đối tượng mới, thêm chúng vào CSDL và đóng chúng lại khi giao dịch chưa kết thúc. Mục đích cơ bản của việc pha trộn là cho phép thực hiện đa tác của nhiều ứng dụng mà một số sử dụng giao dịch, một số thì không.

 Giao dịch và phát sinh đồ hoạ 



Bạn có thể dùng AcTransactionManager::queueForGraphicsFlush()AcTransactionManager::flushGraphics() để vẽ các thực thể theo yêu cầu ngay cả khi chúng liên kết với giao dịch và một số giao dịch vẫn đang hoạt động, có nghĩa là các thay đổi trên thực thể không được hẹn trước. Phương thức AcTransactionManager::queueForGraphicsFlush() sắp xếp tất cả các thực thể eligible liên kết với các giao dịch để cập nhật đồ họa và phương thức AcTransactionManager::flushGraphics() vẽ lại chúng. Bạn có thể dùng AcDbEntity::draw() để vẽ từng thực thể riêng lẻ. Điều này giúp bạn nhìn thấy thực thể trên màn hình mà không phải đợi đến lúc kết thúc quá trình giao dịch ngoài cùng, khi mà mọi thay đổi đều được vẽ lại. Sử dụng AcTransactionManager::enableGraphicsFlush() để bật hoặc tắt chức năng vẽ thực thể. Khi một lệnh kết thúc, bạn sẽ mất quyền kiểm soát đồ họa và nó sẽ tự động kích hoạt lại.

Reactor giao dịch


Trình quản lý giao dịch có một danh sách các phản ứng reactor mà trình thông báo của nó liên quan đến kiểu giao dịch. Dưới đây là các sự kiện gửi thông báo:
  • AcTransactionReactor::transactionStarted()
  • AcTransactionReactor::transactionEnded()
  • AcTransactionReactor::transactionAborted()
  • AcTransactionReactor::endCalledOnOutermostTransaction()
  • AcTransactionReactor::transactionAboutToStart()
  • AcTransactionReactor::transactionAboutToEnd()
  • AcTransactionReactor::transactionAboutToAbort()
  • AcTransactionReactor::objectIdSwapped()
3 thông báo đầu tiên được gửi đi khi một giao dịch, bao giồm cả các giao dịch lồng được khởi động, kết thúc hoặc hủy bỏ. Bạn có thể dùng những thông báo này kết hợp với AcTransactionManager::numActiveTransactions() để xác định các giao dịch có liên quan đến thông báo. Ví dụ, nếu gọi hàm AcTransactionManager::numActiveTransactions() trả về 0 trong hàm chồng AcTransactionReactor::transactionEnded() hoặc AcTransactionReactor::transactionAborted(), bạn có thể biết được giao dịch ngoài cùng đang kết thúc hay bị hủy.
Thông báo endCalledOnOutermostTransaction() báo hiệu kết thúc quá trình thay đổi hẹn trước được thực hiện của tất cả các giao dịch. Bạn có thể sử dụng để sử dụng nó để thực hiện các dọn dẹp cần thiết trước khi quá trình hẹn trước bắt đầu.

Tham số đầu tiên trong tất cả các thông báo thể hiện số lượng các giao dịch đang hoạt động cộng với những giao dịch đã kết thúc thành công. Nó không bao gồm giao dịch vừa bắt đầu hoặc đã bị hủy bỏ.

Không có nhận xét nào:

Đăng nhận xét