2.5 ExamplesThis section provides some complete examples of where you might use autonomous transactions in your applications. 2.5.1 Building an Autonomous Logging MechanismA very common requirement in applications is to keep a log of errors that occur during transaction processing. The most convenient repository for this log is a database table; with a table, all the information is retained in the database and you can use SQL to retrieve and analyze the log. One problem with a database table log, however, is that entries in the log become a part of your transaction. If you perform (or have performed to you) a ROLLBACK, you can easily erase your log. How frustrating! Now, it is true that you can get fancy and use savepoints to preserve your log entries while cleaning up your transaction, but that approach is not only fancy, it is complicated. With autonomous transactions, however, logging becomes simpler, more manageable, and less error prone. Suppose I have a log table defined as follows:
I can use it to store errors (SQLCODE and SQLERRM) that have occurred, or even use it for non-error-related logging. The machine and program columns record information available from the virtual V$SESSION table, as you will see. So I have my table. Now, how should I write to my log? Here's what you should not do:
In other words, never expose your underlying logging mechanism by explicitly inserting into it in your exception sections and other locations. Instead, you should build a layer of code around the table (this is known as encapsulation ). Why do this? Two reasons:
So here is my very simple logging package. It consists of two procedures:
What is the difference between putline and saveline? The log81.saveline procedure (as you will see in the package body) is an autonomous transaction routine, whereas log81.putline simply performs the insert. Here is the package body:
Here are some comments on this implementation that you might find helpful:
With this package in place, my error handler shown earlier can be as simple as this:
No muss, no fuss; developers don't have to concern themselves with the structure of the log table. They don't even have to know they are writing to a database table. And because I have used an autonomous transaction, they can rest assured that no matter what happens in their application, the log entry has been saved. 2.5.2 Using Autonomous Transactions in a Database TriggerThe grand new benefit of autonomous transactions for database triggers is that inside those triggers you can now issue COMMITs and ROLLBACKs, statements that are otherwise not allowed in database triggers. The changes you commit and roll back will not, however, affect the main transaction that caused the database trigger to fire. They will only apply to DML activity taking place inside the trigger itself (or through stored program units called within the trigger). Why would this be of value to you? You may want to take an action in the database trigger that is not affected by the ultimate disposition of the transaction that caused the trigger to fire. For example, suppose that you want to keep track of each action against a table, whether or not the action completed. You might even want to be able to detect which actions failed. Let's see how you can use autonomous transactions to do this. First, let's construct a simple autonomous transaction trigger on the ceo_compensation table that writes a simple message to the following ceo_comp_history table. Here are the two table definitions: /* Filename on companion disk: autontrigger.sql */ CREATE TABLE ceo_compensation ( company VARCHAR2(100), name VARCHAR2(100), compensation NUMBER, layoffs NUMBER); CREATE TABLE ceo_comp_history ( name VARCHAR2(100), description VARCHAR2(255), occurred_on DATE); Here is the before-insert trigger to run all the elements in the script:
With this trigger in place, I can now be certain to track every insert attempt , as shown in the steps below:
You will find in the autontrigger.sql script all the statements needed to create these objects and run your own test. You can even add your CEO's name to the series of INSERTs if he or she fits the bill. 2.5.2.1 Fine-tuning the database triggerBut there is something of a problem with the trigger I just defined. I defined the trigger as an autonomous transaction because I performed the alert in the body of the trigger. But what if I want to perform some additional DML for the main transaction here in the trigger? It won't be rolled back with the rest of the transaction (if a rollback occurs). That won't do at all, from the perspective of data integrity. Generally, I would recommend that you not make a database trigger itself the autonomous transaction. Instead, push all of the independent DML activity (such as writing to the audit or history table) into its own procedure. Make that procedure the autonomous transaction. Have the trigger call the procedure. The autontrigger2.sql script contains the following reworking of the database trigger. First, I create the audit procedure:
Then I change the trigger to the following.
Note the following differences:
As you take advantage of the new autonomous transaction pragma, plan out how you will be using these new code elements. You will almost always be better off hiding the details of your new, independent transactions behind a procedural interface. 2.5.3 Building a Retry CounterSuppose that you want to let a user try to get access to a resource (a file, a row of data, etc.) N times before an outright rejection. You also want to keep track of attempts between connections to the database. The autonomous transaction is a perfect fit, due to the COMMITs required. You will find a simple prototype of a retry mechanism in the retry.pkg file on the companion disk. This mechanism allows you to specify the "item" on which you are placing a limit and keeping track of attempts. These limits are maintained for each unique username. Here is the specification of this package:
The programs are self-explanatory; the implementations are also very straightforward. Here, for example, is the implementation of the procedure that lets you increment the number of attempts. Notice the COMMITs and ROLLBACKs; these are required, since I have used the autonomous transaction pragma.
Here is a script based on the "three strikes" law in California that exercises the package:
Here is the output in SQL*Plus: SQL> @retry.tst Steal car at age 14 ...Receive punishment that fits the crime Caught with a joint at 17 ...Receive punishment that fits the crime Steal pack of cigarettes at age 42 ...Spend rest of life in prison
Copyright (c) 2000 O'Reilly & Associates. All rights reserved. |
|
|