Appendix D. Spring Transaction Support

Using the Spring Framework’s Transactional Annotation

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.

Note

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.

Example D-1. More control of transaction configuration
@Transactional(readOnly=false,
   propagation=Propagation.REQUIRES_NEW,
   isolation=Isolation.SERIALIZABLE,
   rollbackFor={NumberFormatException.class},
   timeout=60)
public abstract void run();

Warning

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.

Transactional annotation attributes

Table D-1 lists the attributes for the Transactional annotation.

Table D-1. List of Transactional annotation attributes
Annotation propertyTypeDescription
isolationIsolationTransaction isolation settings. See Table D-3 below for more information about the available options.
noRollbackForClass[]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.
noRollbackForClassNameString[]
propagationPropagationTransaction propagation configuration. See Table D-2 for more information about this setting.
readOnlybooleanSpecifies whether the transaction is read-only or read-write.
rollbackForClass[]An array of exception classes (or class names) which will cause a transaction rollback. The default behavior is for RuntimeExceptions 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.
rollbackForClassNameString[]
timeoutintNumber of seconds after which a transaction will time out. A timeout value of –1 specifies that the transaction has no timeout (an infinite timeout).

Transaction propagation

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.

Table D-2. Values of the Propagation enumeration
ValueDescription
Propagation.REQUIRED Uses an existing transaction or creates a new one if none exists (this is the default).
Propagation.SUPPORTS Will participate in an existing transaction, but won’t create a new one if none exists.
Propagation.MANDATORY Requires the presence of an existing transaction and throws an exception if none exists.
Propagation.REQUIRES_NEW Creates a new transaction just for this method and suspends the current transaction if one exists.
Propagation.NOT_SUPPORTED Persistence operations in the annotated method will not execute in a transaction. If called within an existing transaction, the transaction is suspended.
Propagation.NEVER Throws an exception if the method is called within a transaction.
Propagation.NESTED If called within an existing transaction, execute the annotated method in a nested transaction.

Tip

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.

Transaction isolation

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.

Table D-3. Values of the Isolation enumeration
ValueDescription
Isolation.DEFAULT Uses the default isolation level of the underlying database.
Isolation.SERIALIZABLE Provides for the highest level of isolation, prevents dirty reads, nonrepeatable reads, and phantom reads. When using this isolation level even read operations may acquire locks in the database. When using this isolation level (or any isolation-level above READ_UNCOMMITTED) you should take care to avoid deadlocks between two concurrent transactions.
Isolation.REPEATABLE_READ Prevents dirty reads and nonrepeatable reads. Does not protect against phantom reads.
Isolation.READ_COMMITTED Prevents dirty reads. Nonrepeatable reads and phantom reads are possible.
Isolation.READ_UNCOMMITTED Lowest level of isolation, one transaction will see uncommitted changes from a second transaction. Dirty reads, nonrepeatable reads, and phantom reads are all possible at this level.

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.

Using a JTA Transaction Manager

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.

Example D-2. Configuring a JTA transaction manager
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/> 
<bean id="transactionManager" 
  class="org.springframework.transaction.jta.JtaTransactionManager" />
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset