Understanding transactions:
Following is the simple example
Explaining transactions in only four words:
ACID stands for:
■ Atomic—Transactions are made up of one or more activities bundled together as a single unit of work. Atomicity ensures that all the operations in the transaction happen or that none of them happen. If all the activities succeed, the transaction is a success. If any of the activities fail, the entire
transaction fails and is rolled back.
■ Consistent—Once a transaction ends (whether successful or not), the system is left in a state that is consistent with the business that it models. The data should not be corrupted with respect to reality.
■ Isolated—Transactions should allow multiple users to work with the same data, without each user’s work getting tangled up with the others. Therefore, transactions should be isolated from each other, preventing concurrent reads and writes to the same data from occurring. (Note that isolation
typically involves locking rows and/or tables in a database.)
■ Durable—Once the transaction has completed, the results of the transaction should be made permanent so that they will survive any sort of system crash.
Understanding Spring’s transaction management support
Spring, like EJB, provides support for both programmatic and declarative transaction management support.
Choosing between programmatic and declarative transaction management is largely a decision of fine-grained control versus convenience. When you program transactions into your code, you gain precise control over transaction boundaries,
beginning and ending them precisely where you want. Typically, you will not
require the fine-grained control offered by programmatic transactions and will
choose to declare your transactions in the context definition file.
Regardless of whether you choose to program transactions into your beans or
to declare them as aspects, you’ll be using a Spring transaction manager to interface with a platform-specific transaction implementation. Let’s see how
Spring’s transaction managers free you from dealing directly with platform specific
transaction implementations.
If you’re using straight JDBC for your application’s persistence, DataSourceTransactionManager
will handle transactional boundaries for you. To use DataSource-
TransactionManager, wire it into your application’s context definition using the
following XML:
Notice that the dataSource property is set with a reference to a bean named
dataSource. Presumably, the dataSource bean is a javax.sql.DataSource bean
defined elsewhere in your context definition file.
Behind the scenes, DataSourceTransactionManager manages transactions by
making calls on the java.sql.Connection object retrieved from the DataSource.
For instance, a successful transaction is committed by calling the commit()
method on the connection. Likewise, a failed transaction is rolled back by calling
the rollback() method.
Hibernate transactions
If your application’s persistence is handled by Hibernate then you’ll want to use
HibernateTransactionManager. For Hibernate 2.x, it is a bean declared with the
following XML:
HibernateTransactionManager delegates responsibility for transaction management
to an org.hibernate.Transaction object that it retrieves from the
Hibernate session. When a transaction successfully completes, HibernateTransactionManager
will call the commit() method on the Transaction object. Similarly,
when a transaction fails, the rollback() method will be called on the
Transaction object.
Java Transaction API transactions
If your transactions span multiple transaction sources (e.g., two or more different databases),
you’ll need to use JtaTransactionManager:
value="java:/TransactionManager" />
JtaTransactionManager delegates transaction management responsibility to a
JTA implementation. JTA specifies a standard API to coordinate transactions
between an application and one or more data sources. The transactionManager-
Name property specifies a JTA transaction manager to be looked up via JNDI.
JtaTransactionManager works with javax.transaction.UserTransaction
and javax.transaction.TransactionManager objects, delegating responsibility
for transaction management to those objects. A successful transaction will be committed
with a call to the UserTransaction.commit() method. Likewise, if the
transaction fails, the UserTransaction’s rollback() method will be called.
By now, I hope you’ve found a Spring transaction manager suitable for your
application’s needs and have wired it into your Spring configuration file. Now it’s
time to put that transaction manager to work. We’ll start by employing the transaction
manager to program transactions manually.
Programming transactions in Spring
public void addRant(Rant rant) {
transactionTemplate.execute(
new TransactionCallback() {
public Object doInTransaction(TransactionStatus ts) {
try {
rant.setPostedDate(new Date());
Vehicle rantVehicle = rant.getVehicle();
Vehicle existingVehicle =
rantDao.findVehicleByPlate(rantVehicle.getState(),
rantVehicle.getPlateNumber());
if(existingVehicle != null) {
rant.setVehicle(existingVehicle);
} else {
rantDao.saveVehicle(rantVehicle);
}
rantDao.saveRant(rant);
} catch (Exception e) {
ts.setRollbackOnly();
}
return null;
}
}
}
To use the TransactionTemplate, you start by implementing the Transaction-
Callback interface. Because TransactionCallback has only one method to
implement, it is often easiest to implement it as an anonymous inner class, as
shown in listing 6.2. As for the code that needs to be transactional, place it within
the doInTransaction() method.
Calling the execute() method on the TransactionTemplate instance will execute
the code contained within the TransactionCallback instance. If your code
encounters a problem, calling setRollbackOnly() on the TransactionStatus
object will roll back the transaction. Otherwise, if the doInTransaction()
method returns successfully, the transaction will be committed.
Where does the TransactionTemplate instance come from? Good question. It
should be injected into RantServiceImpl, as follows:
class="com.roadrantz.service.RantServiceImpl">
…
➥ TransactionTemplate">
ref="transactionManager" />
Declaring transactions
At one time not too long ago, declarative transaction management was a capability
only available in EJB containers. But now Spring offers support for declarative
transactions to POJOs. This is a significant feature of Spring because you now have
an alternative to EJB for declaring atomic operations.
Spring’s support for declarative transaction management is implemented
through Spring’s AOP framework. You can think of a Spring transaction as an aspect that “wraps” a method with transactional boundaries.
Spring provides three ways to declare transactional boundaries in the Spring
configuration. Historically, Spring has always supported declarative transactions
by proxying beans using Spring AOP. But Spring 2.0 adds two new flavors of
declarative transactions: simple XML-declared transactions and annotation-driven
transactions.
We’ll look at all of these approaches to declaring transactions later in this section,
but first let’s examine the attributes that define transactions.
Defining transaction attributes
A transaction attribute is a description of how transaction policies should be applied to a method.
Although Spring provides several mechanisms for declaring transactions, all of them rely on these five
parameters to govern how transactions policies are administered.
Propagation rules answer the question of whether a new transaction should be
started or suspended, or if a method should even be executed within a transactional
context at all.
For example, if a method is declared to be transactional with PROPAGATION_
REQUIRES_NEW behavior, it means that the transactional boundaries are the same
as the method’s own boundaries: a new transaction is started when the method
begins and the transaction ends with the method returns or throws an exception.
If the method has PROPAGATION_REQUIRED behavior, the transactional boundaries
depend on whether a transaction is already under way.
The second dimension of a declared transaction is the isolation level. An isolation
level defines how much a transaction may be impacted by the activities of other
concurrent transactions.
In a typical application, multiple transactions run concurrently, often working
with the same data to get their job done. Concurrency, while necessary, can lead
to the following problems:
■ Dirty read—Dirty reads occur when one transaction reads data that has been
written but not yet committed by another transaction. If the changes are
later rolled back, the data obtained by the first transaction will be invalid.
■ Nonrepeatable read—Nonrepeatable reads happen when a transaction performs
the same query two or more times and each time the data is different.
This is usually due to another concurrent transaction updating the data
between the queries.
■ Phantom reads—Phantom reads are similar to nonrepeatable reads. These
occur when a transaction (T1) reads several rows, and then a concurrent
transaction (T2) inserts rows. Upon subsequent queries, the first transaction
(T1) finds additional rows that were not there before.
In an ideal situation, transactions would be completely isolated from each other,
thus avoiding these problems. However, perfect isolation can affect performance
because it often involves locking rows (and sometimes complete tables) in the
data store. Aggressive locking can hinder concurrency, requiring transactions to
wait on each other to do their work.
Realizing that perfect isolation can impact performance and because not all
applications will require perfect isolation, sometimes it is desirable to be flexible.
Timeout: how long this transaction may run before timing out (and automatically being rolled back by the underlying transaction infrastructure).
Read-only status: a read-only transaction does not modify any data.Read-only transactions can be a useful optimization in some cases (such as when using Hibernate never flush in between).
Rollback rules
The final facet of the transaction pentagon is a set of rules that define what exceptions prompt a rollback and which ones do not. By default, transactions are rolled back only on runtime exceptions and not on checked exceptions. (This behavior is consistent with rollback behavior in EJBs.)
However, you can declare that a transaction be rolled back on specific checked exceptions as well as runtime exceptions. Likewise, you can declare that a transaction not roll back on specified exceptions, even if those exceptions are runtime exceptions.
For more detail
In my next post I will provide all practical examples of declarative transaction.
Reference: Spring in action, Manning Publisher