You can put the Transactional
annotation on
either concrete classes or interfaces, and it can be placed on a
class or a method. If a class is annotated with
Transactional
, the settings apply to every method
defined in that class. If a method is annotated with
Transactional
, the transaction settings apply to a
single method. If the Transactional
annotation is
present on both a class and a method, the settings from the method
annotation take precedence over the class annotation.
From the Transactional
annotation you can
control the isolation level of a transaction, the timeout, the propagation
setting, and an array of exceptions which should cause the transaction to
roll back.
If these terms are unfamiliar, the tables might help explain them.
For example, if we wanted to always create a new transaction with
a serializable isolation level that would time out if not completed in one
minute and would roll back on a NumberFormatException
, we’d write code
like Example D-1.
@Transactional(readOnly=false, propagation=Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE, rollbackFor={NumberFormatException.class}, timeout=60) public abstract void run();
If you use the Transactional
annotation,
you need to be careful where you put the annotation. If you start using
proxies in Spring, or if you start to delve into Spring’s compelling
support for Aspect-Oriented Programming
(AOP), you may want to avoid putting
Transactional
on an interface, and you’ll also
want to be careful about putting Transactional
on nonpublic methods. In
this example, we put the Transactional
annotation on an
interface because we’re not using any of Spring’s AOP
facilities. If you are troubleshooting a system where the Transactional
annotation is being
ignored, the first thing you will want to do is verify public visibility
of the annotated method. There can be some very confusing situations
that arise with proxies and AOP that you may
encounter if you are using AspectJ or the older Spring
AOP facilities. Once you start using Spring’s
AOP facilities, you should start putting
@Transactional
on concrete classes.
Table D-1 lists the attributes for the Transactional
annotation.
Annotation property | Type | Description |
isolation | Isolation | Transaction isolation settings. See Table D-3 below for more information about the available options. |
noRollbackFor | Class[] | An array of exception classes (or class
names) which will not cause a transaction rollback. You can
specify an array of fully-qualified class names or an array of
Class objects. |
noRollbackForClassName | String[] | |
propagation | Propagation | Transaction propagation configuration. See Table D-2 for more information about this setting. |
readOnly | boolean | Specifies whether the transaction is read-only or read-write. |
rollbackFor | Class[] | An array of exception classes (or class
names) which will cause a transaction rollback. The default
behavior is for RuntimeException s to
trigger a rollback, while checked exceptions do not normally
trigger a rollback. If you throw a checked exception and you
want it to trigger a rollback, you’ll need to add it to the
rollbackFor attribute. You
can specify an array of fully-qualified class names or an
array of Class objects. |
rollbackForClassName | String[] | |
timeout | int | Number of seconds after which a transaction will time
out. A timeout value of
–1
specifies that the transaction has no timeout (an infinite
timeout). |
The propagation
attribute tells Spring whether the operation requires a
new transaction, a nested transaction, or can operate within an existing
transaction. Table D-2 lists the valid values for the
propagation
attribute.
Propagation behavior will depend on your transaction provider. If you are using JTA, make sure you read the documentation carefully to see if nested transactions are supported.
The isolation
attribute controls how the locks are acquired within a transaction
and how transactions are affected by concurrent transactions and
statements executed against the database. Table D-3 lists the valid values for the
isolation
attribute.
It is often unrealistic to use SERIALIZABLE
in
a real world system without “serializing” all access to the database.
You are often forced to use either REPEATABLE_READ
or
READ_COMMITTED
and build in some logic to detect
deadlocks and retry operations which have failed. A large multi-user
application (like a web site) that uses serialization needs to be aware
of transaction deadlock, and if you are using any isolation level higher
than READ_UNCOMMITTED
, you should make sure that your
Transactional
annotation defines a finite value
for the timeout
attribute. Isolation behavior depends
on the database you are using. For example, MySQL with the InnoDB
storage engine is going to have a slightly different interpretation of
the isolation levels than different versions of Oracle,
SQL Server, Derby, or HSQLDB.
In the examples in Chapter 13, we are using the HibernateTransactionManager
because
it is straightforward to use and readily available. If you need to use
JTA to participate in distributed transactions or
transactions that span multiple technologies (JDBC +
JMS), you can use the substitute for transactionManager
in your applicationContext.xml as
shown in Example D-2.