Tải bản đầy đủ (.pdf) (13 trang)

Transactions

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (69 KB, 13 trang )

So far we've been talking about all the ways to connect to a database and how to manipulate
data in a database, but we haven't said much about transactions. If your JDBC Connection is in
auto-commit mode, which it is by default, then every DML statement is committed to the database
upon its completion. That may be fine for simple applications, but there are three reasons why
you may want to turn off auto-commit and manage your own transactions:
• To increase performance
• To maintain the integrity of business processes
• To use distributed transactions
First, if you are performing batch insert, update, or delete operations, then turning off auto-commit
and committing results manually at reasonable intervals will improve performance. Note that I
said "at reasonable intervals." If you perform more operations per transaction than can fit into a
rollback segment, the database takes additional time to increase the rollback segment to hold
your uncommitted transaction statements, and that impairs performance.
The second reason why you may want to manage your own transactions is to maintain the
integrity of your business processes. For example, if a customer places an order for some
merchandise, the information you'd need to store for that order would include a list of items to
purchase, billing and shipping information, and an authorized credit card charge. This information
would likely be stored in several different tables. Without a manually managed transaction, it's
possible to enter part of the order, and then the system fails. The rest of the order information is
then lost. This results in an incomplete business transaction.
The third reason why you may want to manage your own transactions is to take advantage of the
benefits of distributed systems. If you have to acquire information from, or update, several
different systems, perhaps of unrelated technology, then you can use Oracle database links to
perform a distributed transaction. Alternatively, you can use an XA connection, which is a
connection based on the X/Open XA Architecture that supports distributed transaction
processing.
In this chapter, we'll look at how to enable manual-transaction support, the scope of transactions,
and their implications for the visibility of database changes. We'll finish with a discussion of the
distributed transaction support provided by Oracle's JDBC implementation. So let's get started
with a look at how to enable manual transactions.
17.1 Manual Transactions


To enable manual- transaction support instead of the auto-commit mode that the JDBC driver
uses by default, use the Connection object's setAutoCommit( ) method. If you pass a
boolean false to setAutoCommit( ), you turn off auto-commit. You can pass a boolean true
to turn it back on again. For example, if you have a Connection object named conn, code the
following to turn off auto-commit:
conn.setAutoCommit(false);
Disabling auto-commit is simple enough, but what are the implications of handling transactions
manually? When is a particular transaction complete, and when does a new one start? And what
effects does a manual transaction have on the implicit locking mechanism of an Oracle
database? Let's begin by defining the scope of a transaction.
17.2 Transaction Scope
An existing transaction ends, and a new transaction starts, at the moment a commit or rollback
command is issued. Assuming you've turned off auto-commit, a COMMIT statement makes
permanent any changes you've made to the database using INSERT, UPDATE, or DELETE
statements since the last time a COMMIT or ROLLBACK statement was executed. With JDBC,
commit your changes manually by calling the Connection object's commit( ) method. Calling
the commit( ) method sends a COMMIT to the database. The commit( ) method takes no
arguments but may throw an SQLException. However, it's very rare for a commit to result in an
exception. For example, to commit changes for the Connection named conn, use the following
code:
conn.commit( );
On the other hand, a rollback command irrevocably discards, or undoes, any INSERT, UPDATE,
or DELETE statements you've executed since the last time a COMMIT or ROLLBACK statement
was executed. To roll back, use the Connection object's rollback( ) method. For example,
to roll back updates to the database made using the Connection named conn, use the
following code:
conn.rollback( );
One last note: while auto-commit is off, if a Connection is closed without committing or rolling
back, or if any DDL is executed, then any uncommitted changes are automatically committed.
17.3 Implicit Locking and Visibility

It's important for you to understand the implications that a transaction has on implicit locking and
visibility. Implicit locking refers to how Oracle automatically locks a database row during an insert,
update, or delete operation. To help you understand the effects of implicit locking, I've created a
table, TEST_TRANS, that has two columns: COL1, which is the table's primary key, and COL2.
Figure 17-1 is a timeline for SQL statements against table TEST_TRANS. On the left are SQL
statements entered from the same transaction and session. On the right are the effects of those
SQL statements on the other session.
Figure 17-1. Implicit locking during manual transactions

To begin, session one performs an insert on table TEST_TRANS:
insert into test_trans ( col1 ) values ( 'X' )
From the moment the INSERT statement is successfully executed, Oracle places an implicit lock
on the new row. Since the new row has not been committed, it is not visible to an UPDATE,
DELETE, or SELECT statement from session two. However, if attempts are made in session two
to insert a row with the same primary key, that session will be blocked indefinitely until the first
transaction is either committed or rolled back. When session one commits, the INSERT statement
in session two will fail with a duplicate key error. After the commit, session one's new row is
visible to an UPDATE, DELETE, or SELECT statement from session two.
Session one now executes an UPDATE statement against the newly inserted and committed row:
update test_trans set col2 = 'A' where col1 = 'X'
Oracle once again places an implicit lock on the row. This time, an INSERT statement from
session two with the same primary key will fail immediately. Furthermore, because of the implicit
lock, any DML statement issued from session two will see a copy of the row as it was before it
was updated by session one's UPDATE statement. However, an UPDATE or DELETE statement
from session two against the same row will be blocked indefinitely until session one commits or
rolls back its changes. When session one commits its UPDATE statement, then any blocked
UPDATE or DELETE statement in session two will execute immediately. After the commit, any
subsequent SELECT statement from session two will see the new row values.
A major concern here is that even though session one's row was locked, when the lock was
released through a commit, session two's UPDATE statement did not take into consideration the

impact of session one's changes. Instead, session one's changes were simply overwritten. The
only way to solve this problem is to use some form of change detection, such as looking at a
timestamp, at an updatestamp, or at all the columns you are modifying with your UPDATE
statement. We'll cover more of this issue in Chapter 18. Keep in mind that you, as the
programmer, are responsible for preventing this type of situation from happening with your code.
Session one continues by executing the following DELETE statement:
delete test_trans where col1 = 'X'
Oracle once again places an implicit lock on the row. This time, any insert, update, or delete from
session two against that row is blocked until session one commits or rolls back its DELETE
statement. Meanwhile, any SELECT statement from session two continues to see the row as it
existed before the delete. When session one commits its DELETE statement, any INSERT
statements from session two that use the same primary key will be successful. Any UPDATE,
DELETE, or SELECT statement from session two will find no rows to affect.
For this scenario, I've assumed that Oracle's default transaction isolation level, which is read
committed, is used. Oracle also supports a serializable level.
17.4 Isolation Levels
When applied to transactions, the term isolation level refers to how well one transaction is
isolated from another. Oracle's default transaction isolation level is read committed. You can get
the current isolation level for a connection by using the Connection object's
getTransactionIsolation( ) method. You can set the transaction isolation level by calling
the Connection object's setTransactionIsolation( ) method and passing one of the two
valid constants: TRANSACTION_READ_COMMITTED or TRANSACTION_SERIALIZABLE. Oracle's
read serializable isolation level will give you a consistent view of data from multiple tables since
the last commit or rollback. Essentially, you see a snapshot of the tables as they existed when the
transaction started. This is as opposed to read committed, which allows you to see changes
during your transaction as soon as other transactions commit.
17.5 Distributed Transactions
A distributed transaction is a set of two or more individual transactions, or branches, managed
externally by a transaction manager and committed or rolled back as a single, global transaction.
When it comes to distributed transactions, you have two choices of how to implement them. If you

need to implement a distributed transaction between two or more Oracle databases, you can use
database links. When using database links, you act as though your distributed transaction is just
another local transaction and let Oracle's two-phase commit mechanism take care of the
distributed transaction process transparently. But what if you want to manage a transaction
between an Oracle and a Sybase database? Or with a credit card processing center? For cases
such as these, you can use the JDBC 2.0 optional package's XAConnection object instead of a
standard Connection object.
Oracle's XA functionality implements the JDBC 2.0 optional package's support for distributed
transactions. Although distributed transaction functionality is typically supported by an application
server, such as one that supports Enterprise JavaBeans (EJB), this does not mean that you can't
take advantage of the XA infrastructure to manage your own distributed transactions.
To create an XAConnection object, use an XADataSource. For the most part, Oracle's
XADataSource is configured just like, and allocates connections just like, the DataSource and
ConnectionPoolDataSource objects we covered in Chapter 7. Therefore, I won't cover
much about establishing a connection using the XA facility, because you can refer to Chapter 7
and the Oracle API for most of what you need to know. Further, XA is typically implemented in a
middle-tier application server, and, as an application developer, you typically use it in a somewhat
transparent way. For example, if you develop EJB, the EJB container uses the XA infrastructure
to manage distributed transactions for your EJB. So for most of us, the XA classes are of little
use. Given that, we won't spend much time on this topic. Instead, just to ensure that you are
familiar with distributed transaction concepts, I'll simply provide an example of an XADataSource
and an XAConnection in a sample program.
Let's take a moment to cover the typical steps taken for a distributed transaction using XA with
two data sources:
1. Each data source, or branch, of the distributed transaction gets an XAConnection
object, which represents a physical connection to a transaction manager, such as a
database, from its XADataSource object.
2. Using the XAConnection object, each branch gets a Connection object that will be
used to perform SQL manipulations.
3. Again, using the XAConnection object, each branch gets an XAResource object, which

will be used to coordinate the distributed transaction with the other branches.
4. A global transaction ID is created that will be used to create Xid objects for each branch
to coordinate a distributed transaction.
5. For each branch, a branch transaction ID is created that will be used along with the
global ID to create an Xid object for each branch.
6. An Xid object is created for each branch using an ID format identifier, a global ID, and a
branch ID.
7. Each branch starts its leg of the distributed transaction by using the XAResource
object's start( ) method, passing it the branch's Xid object. Next, any desired SQL
statements are executed. The branch's leg is then ended by calling the XAResource
object's end( ) method.
8. After each branch of a distributed transaction completes its SQL operations, it each calls
its XAResource object's prepare( ) method to prepare for a global commit or rollback
operation.
9. If all branches report a successful prepare phase, then each branch calls its
XAResource object's commit( ) method. Otherwise, it calls its XAResource object's
rollback( ) method.
Now that you have the big picture of how distributed transactions are implemented using XA, let's
look at the details. We'll start with the optional package's XADataSource interface.
17.5.1 XA Data Sources
The XADataSource interface is implemented with the OracleXADataSource class and uses
the same properties as the DataSource and ConnectionPoolDataSource classes we
discussed in Chapter 7. XADataSource objects are factories for XAConnection objects,
which in turn are factories for Connection and XAResource objects. Instead of the two
overloaded getConnection( ) methods found in the DataSource interface, the
XADataSource interface has two overloaded getXAConnection( ) methods with the
following signatures. Keep in mind that they, like all JDBC methods, can throw a SQLException.
XAConnection getXAConnection( )
XAConnection getXAConnection(String username, String Password)
You can configure JNDI to allocate XADataSource objects just as we did for DataSource

objects in Chapter 7. After you've created an XADataSource object, your next step is to
allocate an XAConnection using one of the two getXAConnection( ) methods.
17.5.2 XA Connections
An XAConnection extends PooledConnection and is implemented by the OracleXA-
Connection class. An XAConnection represents a physical database connection. Seeing that
an XAConnection object extends a PooledConnection, it inherits all of the
PooledConnection methods:
addConnectionEventListener( )
close( )
getConnection( )
removeConnectionEventListener( )
In addition to the PooledConnection methods, the XAConnection interface defines one more
method, getXAResource( ). Following is the additional method signature. As usual, the
method can throw a SQLException.
XAResource getXAResource( )
Like the connection returned by a PooledConnection object, the Connection object returned
by an XAConnection is not the same type of Connection object returned by DriverManager.
At least, they are not the same internally. However, the two Connection objects implement the
same Connection interface, so they appear to be the same. The difference is that the close(
) method of a Connection object from an XAConnection object returns a connection to
XAConnection, an intermediary that provides the hooks for distributing a transaction and does
not necessarily close the physical connection to a database. Calling XAConnection object's
close( ) method actually closes the physical connection to a database.

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×