Book Image

Mockito for Spring

By : Sujoy Acharya
Book Image

Mockito for Spring

By: Sujoy Acharya

Overview of this book

Table of Contents (12 chapters)
Mockito for Spring
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Handling a transaction with Spring


Spring Framework provides supports for transaction management. The following are characteristics of the Spring transaction management framework:

  • Offers abstraction for transaction management

  • Defines a programming model that supports different transaction APIs, such as JDBC, JTA, and JPA

  • Declarative transaction management is supported

  • Provides a simpler programmatic transaction management API

  • Easily integrates with Spring's data access abstractions

Two transaction management options are available for the J2EE developers. The following are the two options:

  • The application server manages global transactions, using the Java Transaction API (JTA). It supports multiple transaction resources, such as database transactions, JMS transactions, and XA transactions.

  • Resource-specific local transactions, such as a transaction associated with a JDBC connection.

Both transaction models have downsides. The global transaction needs an application server and JNDI to manage transactions; it uses JTA but the JTA API is cumbersome and has a complex exception model. The need for an application server, JNDI, and JTA limits the reusability of code.

The local transactions have the following disadvantages:

  • Cannot handle multiple transactional resources

  • Invasive to the programming model

Spring's transaction model solves the problems associated with the global and local transactions, and it offers a consistent programming model for developers that can be used in any environment.

Spring Framework supports both declarative and programmatic transaction management. Declarative transaction management is the recommended one, and it has been well accepted by the development community.

The programmatic transaction model provides an abstraction that can be run over any underlying transaction infrastructure. The concept of transaction strategy is the key to the transaction abstraction. The org.springframework.transaction.PlatformTransactionManager interface defines the strategy.

The following is the PlatformTransactionManager interface:

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(
    TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws 
                                         TransactionException;
    void rollback(TransactionStatus status) throws 
                                         TransactionException;
}

The following are the characteristics of PlatformTransactionManager:

  • PlatformTransactionManager is not a class; instead, it is an interface, and thus it can be easily mocked or stubbed to write tests.

  • It doesn't need a JNDI lookup strategy, as its implementations can be defined as Spring beans in Spring Framework's IoC container.

  • Methods defined in PlatformTransactionManager throw TransactionException. However, this is an unchecked exception, so programmers are not forced to handle the exception. But in reality, the exception is fatal in nature; when it is thrown, there is very little chance that the failure can be recovered.

  • The getTransaction() method takes a TransactionDefinition parameter and returns a TransactionStatus object. The TransactionStatus object can be a new or an existing transaction.

The TransactionDefinition interface defines the following:

public interface TransactionDefinition {
   int getIsolationLevel();
   int getPropagationBehavior();
   String getName();
   int getTimeout();
   boolean isReadOnly();
}
  • Isolation: This returns the degree of isolation of this transaction from other transactions. The following are the Spring propagations:

    • ISOLATION_DEFAULT

    • ISOLATION_READ_COMMITTED

    • ISOLATION_READ_UNCOMMITTED

    • ISOLATION_REPEATABLE_READ

    • ISOLATION_SERIALIZABLE

  • Propagation: This returns the transaction propagation behavior. The following are the allowable values:

    • PROPAGATION_MANDATORY: This needs a current transaction and raises an error if no current transaction exists

    • PROPAGATION_NESTED: This executes the current transaction within a nested transaction

    • PROPAGATION_NEVER: This doesn't support a current transaction and raises an error if a current transaction exists

    • PROPAGATION_NOT_SUPPORTED: This executes code non-transactionally

    • PROPAGATION_REQUIRED: This creates a new transaction if no transaction exists

    • PROPAGATION_REQUIRES_NEW: This suspends the current transaction and creates a new transaction

    • PROPAGATION_SUPPORTS: If the current transaction exists, then this supports it; otherwise, it executes the code non-transactionally

    • TIMEOUT_DEFAULT: This uses the default timeout

  • Timeout: This returns the maximum time in seconds that the current transaction should take; if the transaction takes more than that, then the transaction gets rolled back automatically.

  • Read-only status: This returns whether the transaction is a read-only transaction. A read-only transaction does not modify any data.

The TransactionStatus interface provides a simple way for transactional code to control the transaction execution and query the transaction status; it has the following signature:

public interface TransactionStatus {
    boolean isNewTransaction();
    void setRollbackOnly();
    boolean isRollbackOnly();
}

The PlatformTransactionManager implementations normally require knowledge of the environment in which they work, such as JDBC, JTA, Hibernate, and so on.

A local PlatformTransactionManager implementation defines a JDBC data source and then uses the Spring DataSourceTransactionManager class, which gives it a reference to DataSource. The following Spring context defines a local transaction manager:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
  <property name="driverClassName" value="${jdbc.driverClassName}" /> 
  <property name="url" value="${jdbc.url}" /> 
  <property name="username" value="${jdbc.username}" /> 
  <property name="password" value="${jdbc.password}" /> 
</bean>

Here, ${jdbc.xxxx} represents the values defined in the properties file. Usually, the convention is that the JDBC properties are defined in a properties file that is then loaded from applicationContext, and then the JDBC properties are accessed using the key such as ${key}. The following is the XML configuration of transaction manager:

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

When we use JTA in a J2EE container and use a container DataSource obtained via the JNDI lookup, in conjunction with Spring's JtaTransactionManager, then JtaTransactionManager doesn't need to know about DataSource, or any other specific resources, as it will use the container's global transaction management infrastructure.

The following is the JtaTransactionManager definition in Spring context:

<jee:jndi-lookup id="dataSource" jndi-name="myDataSource "/> 
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> 

The benefit of Spring transaction manager is that in all cases, the application code will not need to change at all. We can change how transactions are managed merely by changing the configuration, even if that change means moving from local to global transactions or vice versa.

Declarative transaction management is preferred by most users; it is the option with the least impact on the application code. It is most consistent with the ideals of a non-invasive lightweight container. Spring's declarative transaction management is made possible with Spring AOP.

The similarities between the EJB CMT and Spring declarative transaction are as follows:

  • It is possible to specify transaction behavior down to the individual method level

  • It is possible to make a setRollbackOnly() call within a transaction context if necessary

Working with declarative Spring transaction

We'll create a simple Spring transaction management project and learn about the basics. The following are the steps to create the project:

  1. Create an empty class, Foo, under the com.packt.tx package. The following is the class body:

    package com.packt.tx;
    
    public class Foo {
    
    }
  2. Create an interface, FooService, to handle the CRUD operations on Foo:

    package com.packt.tx;
    
    public interface FooService {
    
      Foo getFoo(String fooName);
    
      void insertFoo(Foo foo);
    
      void updateFoo(Foo foo);
    
    }
  3. Create a default implementation of FooService, and from each method, throw UnsupportedOperationException to impersonate a rollback transaction:

    public class FooServiceImpl implements FooService {
    
      @Override
      public Foo getFoo(String fooName) {
        throw new UnsupportedOperationException();
      }
      @Override
      public void insertFoo(Foo foo) {
        throw new UnsupportedOperationException();
    
      }
      @Override
      public void updateFoo(Foo foo) {
        throw new UnsupportedOperationException();
    
      }
    
    }
  4. Create an application context file called applicationContextTx.xml directly under the src folder and add the following entries:

    Define the fooService bean:

    <bean id="fooService" class="com.packt.tx.FooServiceImpl" />

    Define a Derby data source:

    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
        <property name="url" value="jdbc:derby:derbyDB;create=true" />
        <property name="username" value="dbo" />
        <property name="password" value="" />
    </bean>

    Define a transaction manager with the data source:

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

    Define an advice with transaction manager so that all get methods will have a read-only transaction:

    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
        <!--all methods starting with 'get' are read-only-->
          <tx:method name="get*" read-only="true" />
          <tx:method name="*" />
        </tx:attributes>
      </tx:advice>

    Define the AOP configuration to apply the advice on pointcut:

      <aop:config>
        <aop:pointcut id="fooServiceOperation"
          expression="execution(* com.packt.tx.FooService.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation" />
      </aop:config>
    </beans>
  5. Create a test class to get the FooService bean and call the getFoo method on the FooService bean. The following is the class:

    public class TransactionTest {
    
      public static void main(String[] args) {
        AbstractApplicationContext context = new 
          ClassPathXmlApplicationContext(
            "applicationContextTx.xml");
    
        FooService fooService = (FooService) 
          context.getBean("fooService");
        System.out.println(fooService);
        fooService.getFoo(null);
      }
    }
  6. When we run the program, Spring creates a transaction and then rolls back the transaction as it throws UnsupportedOperationException. Check the log to get the details. The following is the log:

    - Creating new transaction with name [com.packt.tx.FooServiceImpl.getFoo]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
    - Acquired Connection [341280385, URL=jdbc:derby:derbyDB, UserName=dbo, Apache Derby Embedded JDBC Driver] for JDBC transaction
    - Setting JDBC Connection [341280385, URL=jdbc:derby:derbyDB, UserName=dbo, Apache Derby Embedded JDBC Driver] read-only
    - Switching JDBC Connection [341280385, URL=jdbc:derby:derbyDB, UserName=dbo, Apache Derby Embedded JDBC Driver] to manual commit
    - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@6b58ba2b] for key [org.apache.commons.dbcp2.BasicDataSource@680624c7] to thread [main]
    - Initializing transaction synchronization
    - Getting transaction for [com.packt.tx.FooServiceImpl.getFoo]
    - Completing transaction for [com.packt.tx.FooServiceImpl.getFoo] after exception: java.lang.UnsupportedOperationException
    - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
    - Winning rollback rule is: null
    - No relevant rollback rule found: applying default rules
    - Triggering beforeCompletion synchronization
    - Initiating transaction rollback
    - Rolling back JDBC transaction on Connection [341280385, URL=jdbc:derby:derbyDB, UserName=dbo, Apache Derby Embedded JDBC Driver]

Exploring transaction attributes

We declared a transaction advice and its attributes in the preceding example. This section examines the transaction attributes such as propagation, isolation, read-only, timeout, and rollback rules.

Transaction propagation has seven levels:

  • PROPAGATION_MANDATORY: Method should run in a transaction and if nothing exists, an exception will be thrown.

  • PROPAGATION_NESTED: Method should run in a nested transaction.

  • PROPAGATION_NEVER: The current method should not run in a transaction. If this exists, an exception will be thrown.

  • PROPAGATION_NOT_SUPPORTED: Method should not run in a transaction. The existing transaction will be suspended till the method completes the execution.

  • PROPAGATION_REQUIRED: Method should run in a transaction. If this already exists, the method will run in that, and if not, a new transaction will be created.

  • PROPAGATION_REQUIRES_NEW: Method should run in a new transaction. If this already exists, it will be suspended till the method finishes.

  • PROPAGATION_SUPPORTS: Method need not run in a transaction. If this already exists, it supports one that is already in progress.

The following are the isolation levels:

  • ISOLATION_DEFAULT: This is the default isolation specific to the data source.

  • ISOLATION_READ_UNCOMMITTED: This reads changes that are uncommitted. This leads to dirty reads, phantom reads, and non-repeatable reads.

    A dirty read happens when a transaction is allowed to read data from a row that has been modified by another running transaction and not yet committed.

    Data getting changed in the current transaction by other transactions is known as a phantom read.

    A non-repeatable read means data that is read twice inside the same transaction cannot be guaranteed to contain the same value.

  • ISOLATION_READ_COMMITTED: This reads only committed data. Dirty reads are prevented but repeatable and non-repeatable reads are possible.

  • ISOLATION_REPEATABLE_READ: Multiple reads of the same field yield the same results unless modified by the same transaction. Dirty and non-repeatable reads are prevented but phantom reads are possible as other transactions can edit the fields.

  • ISOLATION_SERIALIZABLE: Dirty, phantom, and non-repeatable reads are prevented. However, this hampers the performance of the application.

The read-only attribute specifies that the transaction is only going to read data from a database. It can be applied to only those propagation settings that start a transaction, that is, PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW, and PROPAGATION_NESTED.

The timeout specifies the maximum time allowed for a transaction to run. This is required for the transactions that run for very long and hold locks for a long time. When a transaction reaches the timeout period, it is rolled back. The timeout needs to be specified only on propagation settings that start a new transaction.

We can specify that transactions will roll back on certain exceptions and do not roll back on other exceptions by specifying the rollback rules.

Using the @Transactional annotation

The functionality offered by the @Transactional annotation and the support classes is only available in Java 5 (Tiger) and above. The @Transactional annotation can be placed before an interface definition, a method on an interface, a class definition, or a public method on a class. A method in the same class takes precedence over the transactional settings defined in the class-level annotation.

The following example demonstrates the method-level precedence:

@Transactional(readOnly = true) 
public class FooServiceImpl implements FooService { 
    public Foo getFoo(String fooName) { 
        
    } 
    // This settings has precedence for this method 
    @Transactional(readOnly = false, propagation = 
      Propagation.REQUIRES_NEW) 
    public void updateFoo(Foo foo) { 
    } 
}

However, the mere presence of the @Transactional annotation is not enough to actually turn on the transactional behavior; the @Transactional annotation is simply metadata that can be consumed by something that is aware of @Transactional and that can use the metadata to configure the appropriate beans with the transactional behavior.

The default @Transactional settings are as follows:

  • The propagation setting is PROPAGATION_REQUIRED

  • The isolation level is ISOLATION_DEFAULT

  • The transaction is read/write

  • The transaction timeout defaults to the default timeout of the underlying transaction system, or none if timeouts are not supported

  • Any RuntimeException will trigger a rollback and any checked exception will not trigger a rollback

When the previous POJO is defined as a bean in a Spring IoC container, the bean instance can be made transactional by adding one line of XML configuration. We'll examine the @Transactional annotation in the following example:

  1. Create a application context file called applicationContextTxAnnotation.xml and add the following lines (no need for aop and advice):

    <context:annotation-config />
    <bean id="fooService" class="com.packt.tx.FooServiceImpl" />
    
    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager" />
    
    <bean id="dataSource" 
      class="org.apache.commons.dbcp2.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" 
        value="org.apache.derby.jdbc.EmbeddedDriver" />
        <property name="url" 
        value="jdbc:derby:derbyDB;create=true" />
        <property name="username" value="dbo" />
        <property name="password" value="" />
    </bean>
    
    <bean id="txManager"
      class="org.springframework.jdbc.datasource
        .DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
  2. Annotate FooServiceImpl with the @Transactional annotation:

    @Transactional
    public class FooServiceImpl implements FooService {
    
      @Override public Foo getFoo(String fooName) {
        throw new UnsupportedOperationException();
      }
    
      @Override public void insertFoo(Foo foo) {
        throw new UnsupportedOperationException();
           }
    
      @Override public void updateFoo(Foo foo) {
        throw new UnsupportedOperationException();
      }
    }
  3. Create a class called TransactionTestAnnotation, load applicationContextTxAnnotation, and examine whether the same log appears. The following is the class:

    public class TransactionTestAnnotation {
    
      public static void main(String[] args) {
        AbstractApplicationContext context = new 
        ClassPathXmlApplicationContext(
            "applicationContextTxAnnotation.xml");
    
        FooService fooService = (FooService) 
          context.getBean("fooService");
        System.out.println(fooService);
        fooService.getFoo(null);
      }
    }

Working with a programmatic Spring transaction

Spring provides two means of programmatic transaction management:

  • Using TransactionTemplate

  • Using a PlatformTransactionManager implementation directly

The Spring team generally recommends the first approach (using TransactionTemplate).

The second approach is similar to using the JTA UserTransaction API (although exception handling is less cumbersome).

Using TransactionTemplate

The following are the characteristics of TransactionTemplate:

  • It adopts the same approach as other Spring templates such as JdbcTemplate and HibernateTemplate

  • It uses a callback approach

  • A TransactionTemplate instance is threadsafe

The following code snippet demonstrates TransactionTemplate with a callback:

Object result = transTemplate.execute(new TransactionCallback() { 
    public Object doInTransaction(TransactionStatus status) { 
        updateOperation(); 
        return resultOfUpdateOperation(); 
    } 
});

If there is no return value, use the convenient TransactionCallbackWithoutResult class via an anonymous class, as follows:

transTemplate.execute(new TransactionCallbackWithoutResult() { 
protected void doInTransactionWithoutResult(
                            TransactionStatus status) { 
        updateOperation1(); 
        updateOperation2(); 
    } 
});

Application classes wishing to use TransactionTemplate must have access to PlatformTransactionManager, which will typically be supplied to the class via a dependency injection. It is easy to unit test such classes with a mock or stub PlatformTransactionManager. There is no JNDI lookup here; it is a simple interface. As usual, you can use Spring to greatly simplify your unit testing.

Using PlatformTransactionManager

A PlatformTransactionManager implementation can be directly used to manage a transaction:

  1. Simply pass the implementation of the PlatformTransactionManager to your bean via a bean reference.

  2. Then, using the TransactionDefinition and TransactionStatus objects, you can initiate transactions and perform a rollback or commit.

The following code snippet provides an example of such use:

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
    // execute your business logic here
} catch (Exception ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.