Relevance of Spring Transaction

Enterprise Java application servers natively provide JTA (Java Transaction API) support, which enables distributed transaction, which is also known as global transaction, spanning multiple resources, applications and servers. Traditionally, Enterprise Java Beans (EJB) and Message Driven Beans (MDB) were used for container-managed transactions (CMT), which is based on JTA and JNDI. JTA transaction management is resource-intensive; its exception handling is based on checked exceptions and so is not developer-friendly. Moreover, unit testing is hard with EJB CMT.

For those who do not want to use resource-intensive JTA transactions, a local transaction is another available option, and one that allows you to programmatically enforce resource-specific transactions using APIs such as JDBC. Although relatively easy to use, it is limited to a single resource, as multiple resources cannot participate in a single transaction. Moreover, local transactions are often invasive, hence they pollute your code.

Spring Transaction abstraction solves the problems of global and local transactions by providing a consistent transaction model that can run in any environment. Although it supports both declarative and programmatic transaction management, the declarative model is sufficient for most cases. Spring Transaction eliminates the need for an application server such as JBoss or WebLogic just for transactions. You can start with local transactions using Spring on a simple Servlet engine such as Tomcat and scale it up later to distributed transactions on an application server without touching your business code, just by changing the transaction manager in your Spring metadata.

Most applications just need local transactions since they do not deal with multiple servers or transactional resources such as databases, JMS, and JCA; hence, they do not need a full-blown application server. For distributed transactions spanned across multiple servers over remote calls, you need JTA, necessitating an application server, as JTA needs JNDI to look up the data source. JNDI is normally available only in an application server. Use JTATransactionManager inside application servers for JTA capabilities.

Note

When you deploy your Spring application inside an application server, you can use server-specific transaction managers to utilize their full features. Just switch the transaction manager to use server-specific JtaTransactionManager implementations such as WebLogicJTATransactionManager and WebSphereUowTransactionManager inside your Spring metadata. All your code is completely portable now.

Spring Transaction fundamentals

Spring Transaction Management abstraction is designed around an interface named PlatformTransactionManager, which you need to configure as a Spring bean in your Spring metadata. PlatformTransactionManager manages the actual transaction instance that performs the transaction operations such as commit and rollback, based on a TransactionDefinition instance that defines the transaction strategy. TransactionDefinition defines the critical transaction attributes such as isolation, propagation, transaction timeout, and the read-only status of a given transaction instance.

Note

Transaction attributes determine the behavior of transaction instances. They can be set programmatically as well as declaratively. Transaction attributes are:

Isolation level: Defines how much a transaction is isolated from (can see) other transactions running in parallel. Valid values are: None, Read committed, Read uncommitted, Repeatable reads, and Serializable. Read committed cannot see dirty reads from other transactions.

Propagation: Determines the transactional scope of a database operation in relation to other operations before, after, and nested inside itself. Valid values are: REQUIRED, REQUIRES_NEW, NESTED, MANDATORY, SUPPORTS, NOT_SUPPORTED, and NEVER.

Timeout: Maximum time period that a transaction can keep running or waiting before it completes. Once at timeout, it will roll back automatically.

Read-only status: You cannot save the data read in this mode.

These transaction attributes are not specific to Spring, but reflect standard transactional concepts. The TransactionDefinition interface specifies these attributes in the Spring Transaction Management context.

Depending on your environment (standalone, web/app server) and the persistence mechanism you use (such as plain JDBC, JPA, and Hibernate), you choose the appropriate implementation of PlatformTransactionManager and configure it as required, in your Spring metadata. Under the hood, using Spring AOP, Spring injects TransactionManager into your proxy DAO (or EntityManager, in the case of JPA) and executes your transactional methods, applying transaction semantics declared in your Spring configuration, either using the @Transactional annotation or the equivalent XML notations. We will discuss the @Transactional annotation and its XML equivalent later on in this chapter.

For applications that operate on a single DataSource object, Spring provides DataSourceTransactionManager. The following shows how to configure it in XML:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="taskifyDS" />
</bean>

For multiple DataSource objects or transactional resources, you need a JtaTransactionManager with JTA capabilities, which usually delegates to a container JTA provider. You need to use DataSource objects in Java EE application servers, defined with the server, and looked up via JNDI along with JtaTransactionManager. A typical combination should look like the following code fragment:

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
</bean>
<jee:jndi-lookup id="taskifyDS" jndi-name="java:jboss/datasources/taskify" expected-type="javax.sql.DataSource/>

If you are using Hibernate and just a single DataSource (and no other transactional resource), then the best option is to use HibernateTransactionManager, which requires you to pass the session factory as a dependency. For JPA, Spring provides JpaTransactionManager, which binds a single JPA EntityManager instance. However, it is advisable to use JtaTransactionManager in application-container environments.

Spring provides specialized transaction managers for application servers for WebLogic and WebSphere in order to leverage full power from container-specific transaction coordinators. Use WebLogicTransactionManager and WebsphereUowTransactionManager in the respective environments.

Declarative transaction management

Separating Transaction semantics out of your business code into an XML file or annotations above the methods is usually called declarative transaction management. Spring Framework allows you to apply transactional behavior into your beans transparently and non-invasively using its declarative transaction management feature.

You can apply Spring Transaction declaratively on any Spring bean, unlike EJB CMT. With Spring Transaction, you can specify transactional advices around your bean methods inside the metadata in an AOP style; then Spring will apply your those advices at runtime using AOP. You can set rollback rules to specify which exceptions around which beans or methods cause automatic rollback or non-rollback.

Transactional modes – proxy and AspectJ

Spring Transactions supports two transactional modes: proxy mode and AspectJ mode. Proxy is the default and most popular mode. In proxy mode, Spring creates an AOP proxy object, wrapping the transactional beans, and applies transactional behavior transparently around the methods using transaction aspects based on the metadata. The AOP proxy created by Spring based on transactional metadata, with the help of the configured PlatformTransactionManager, performs transactions around the transactional methods.

If you choose AspectJ mode for transactions, the transactional aspects are woven into the bean around the specified methods modifying the target class byte code during compile-time. There will be no proxying in this case. You will need AspectJ mode in special cases such as invoking transactional methods of the same class with different propagation levels, where proxying would not help.

Defining transactional behavior

Spring offers two convenient approaches for declaratively defining the transactional behavior of your beans:

  • AOP configuration for transactions in an XML metadata file
  • Using the @Transactional annotation

Let's start with AOP configuration in an XML file. Refer to the Aspect Oriented Programming section of Chapter 1, Getting Started with Spring Core, for a detailed discussion of configuring AOP, using aspects, pointcuts, advice, and so on.

Typically, you declare transaction advices and pointcuts with pointcut expressions in your XML metadata file. The best approach is to keep the transaction configuration in a separate bean-definition file (for example, transation-settings.xml) and import it into your primary application-context file.

Typically, you declare transactional advices and other semantics as shown in the following code:

<!-- transactional advices --> 
<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <!-- the transactional semantics... -->
  <tx:attributes>
    <!-- all methods starting with 'get' are read-only -->
    <tx:method name="find*" read-only="true" />
    <!-- other methods use the default transaction settings (see below) -->
    <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" />
  </tx:attributes>
</tx:advice>

<!-- Applying the above advices to the service layer methods -->
<aop:config>
  <aop:pointcut id="allServiceMethods"
  expression="execution(* com.taskify.service.*.*(..))" />
  <aop:advisor advice-ref="txAdvice" pointcut- ref="allServiceMethods" />
</aop:config>

You can see that this AOP configuration instructs Spring how to weave transactional advices around the methods using pointcuts. It instructs TransactionManager to make all find methods of the entire service layer read-only, and to force other methods to have the transaction propagation: REQUIRED, which means that, if the caller of the method is already in a transactional context, this method joins the same transaction without creating a new one; otherwise, a new transaction is created. If you want to create a different transaction for this method, you should use the REQUIRES_NEW propagation.

Also, note that the transaction isolation level is specified as DEFAULT, which means the default isolation of the database is to be used. Most databases default to READ_COMMITTED, which means a transactional thread cannot see the data of other transactions in progress (dirty reads).

Setting rollback rules

With Spring transaction, you can set rollback rules declaratively, in the same <tx:advice> block, as shown in the following code:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    ...
    <tx:method name="completeTask" propagation="REQUIRED" rollback-for="NoTaskFoundException"/>
    <tx:method name="findOpenTasksByAssignee" read-only="true" no-rollback-for="InvalidUserException"/>
    <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" />
  </tx:attributes>
</tx:advice>

You can specify which exceptions should or should not rollback transactions for your business operations using the rollback-for and no-rollback-for attributes of the <tx:method> element.

Note

TransactionException thrown by the PlatformTransactionManager interface's methods is the unchecked exception, RuntimeException. In Spring, transactions rollback for unchecked exceptions automatically. Checked, or application exceptions are not rolled back unless specified in the metadata, using the rollback-for attribute.

Spring Transaction allows you to customize the transactional behavior of your beans to a minute level of granularity using Spring AOP and SpEL. Moreover, you can specify the behavioral attributes of your transaction such as propagation, isolation, and timeout at the method level on the <tx:method> element.

Using the @Transactional annotation

The @Transactional annotation describes transactional attributes on a method or class. Class-level annotation applies to all methods unless explicitly annotated at method level. It supports all the attributes you otherwise set at the XML configuration. See the following example:

@Service
@Transactional
public class TaskServiceImpl implements TaskService {
  ...
  public Task createTask(Task task) {
    if (StringUtils.isEmpty(task.getStatus()))
      task.setStatus("Open");
    taskDAO.save(task);
    return task;
  }

  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = NoUserFoundException)
  public Task createTask(String name, int priority, Long createdByuserId, Long assigneeUserId, String comments) {
    Task task = new Task(name, priority, "Open", userService.findById(createdByuserId), null, userService.findById(assigneeUserId), comments);
    taskDAO.save(task);
    logger.info("Task created: " + task);
    return task;
  }

  @Transactional(readOnly = true)
  public Task findTaskById(Long taskId) {
    return taskDAO.findOne(taskId);
  }
  ...
}

In the preceding example, the transactional method createTask with propagation REQUIRED rolls back for NoUserFoundException. Similarly, you can set no-rollback rules at the same level too.

Note

@Transactional can be applied only to public methods. If you want to annotate over protected, private, or package-visible methods, consider using AspectJ, which uses compile-time aspect weaving. Spring recommends annotating @Transactional only on concrete classes as opposed to interfaces, as it will not work in most cases such as when you use proxy-target-class="true" or mode="aspectj".

Enabling transaction management for @Transactional

You need to first enable transaction management in your application before Spring can detect the @Transactional annotation for your bean methods. You enable transaction in your XML metadata using the following notation:

<tx:annotation-driven transaction-manager="transactionManager" />

The following is the Java configuration alternative for the preceding listing:

@Configuration
@EnableTransactionManagement
public class JpaConfiguration {
}

Spring scans the application context for bean methods annotated with @Transactional when it sees either of the preceding settings.

You can change the transaction mode from proxy, which is the default, to aspectj at this level:

<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>

Another attribute you can set at this level is proxy-target-class, which is applicable only in the case of the proxy mode.

Programmatic transaction management

Spring provides comprehensive support for programmatic transaction management using two components: TransactionTemplate and PlatformTransactionManager. The following code snippet illustrates the usage of TransactionTemplate:

@Service
public class TaskServiceImpl implements TaskService {
  @Autowired
  private TransactionTemplate trxTemplate;
  ...
  public Task createTask(String name, int priority, Long createdByuserId, Long assigneeUserId, String comments) {

    return trxTemplate.execute(new TransactionCallback<Task>() {
      @Override
      public Task doInTransaction(TransactionStatus status) {
        User createdUser = userService.findById(createdByuserId);
        User assignee = userService.findById(assigneeUserId);
        Task task = new Task(name, priority, "Open", createdUser, null, assignee, comments);
        taskDAO.save(task);
        logger.info("Task created: " + task);
        return task;
      }
    });
  }
}

TransactionTemplate supports the setting of all transaction attributes, as in the case of XML configuration, which gives you more granular control at the expense of mixing your business code with transactional concerns. Use it only if you need absolute control over a particular feature that cannot be achieved with declarative transaction management. Use declarative transaction management if possible, for better maintainability and management of your application.

..................Content has been hidden....................

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