Saturday, March 10, 2018

Spring Transaction Support

Spring transaction framework provides a consistent abstraction across transaction management API’s. Any application extensively requires transaction management capabilities to handle its business operations consistently.

In a traditional Java or J2ee programming, developer has two choices for transaction management. Those are Global or Local transactions; each of them has specific use cases where they have to be used.

Global Transaction

Global Transactions are also called as XA Transactions, a transaction is said to be Global or XA only when multiple resources are involved in it. Typically the resources would be a database and a message queues etc. When we say these two resources are involved in a transaction, it means performing the operation on a database and on a messaging queue should be part of single transaction and if a roll back happens it should roll back the operations on both the resources rather than one.


The participating resources of a global transaction must be also XA resources rather than local resources. For example while configuring a Datasource to get the connection, the application server will give you an option of selecting the Datasource as XA or non-XA, this indicates whether the connections that are created out of that datasource will participate in global transactions or not.
Two-Phase commit

Global transaction will use the two phase commit process. This indicates the global transaction will be managed by a transaction coordinator (also called as TransactionManager). Every resource in the transaction is maintained by the ResourceManager and executes the transaction as described below.

Phase – 1

a) Each ResourceManager coordinates the local operations and writes them to log.
b) If successful operations, response is OK.

Phase – 2

a) Coordinator issues commit on all resource managers.
b) Participates complete the commit process and writes to log file.
c) If all ResourceManager’s responds OK.

Otherwise

a) Coordinator issues roll back to all the resourcemanagers.
b) Participants undo’s all the operations locally.

In order to work with Global Transactions or Distributed transactions, you need a Transaction Coordinator which will be maintained by a J2EE Server. To work with XA Transactions, J2EE has provided api’s like JTA or EJB etc.

In JTA you need to programmatically manage transactions, and the API complex to work with and need to perform JNDI lookup to get the TransactionCordinator from J2EE server.

You can use declarative transaction management using EJB’s are heavy wait components and may not be appropriate for whom who are looking for only Global Transaction solution (as EJB are distributed components).

Local Transaction

Local Transaction is transaction which involves only one resource as part of transactional boundary. If you are working with database and message queue each and every operation that has been done are committed separately, rather than part of single transaction. The above figure depicts the same.

In a local transaction, the commit of a transaction will be done on the resource directly for example while you are working on JDBC, you will issue a commit on directly the connection using con.commit().

Benefit of Spring Transaction

By the above we understood that working with global and local transactions need two different API’s and need to learn two different API’s. your code is tightly coupled with one transaction implementation, and if you want to switch between local and global or with in global you need to modify your code. Instead spring transaction management capability has provided a unified approach that allows you to work with local or global transactions transparently.

Spring has provided three ways of working with transactions.
     1)  Programmatic transaction management
     2)  Declarative transaction management
     3)  Annotation driven transaction management
Out of which developers extensively uses declarative transaction management.

Declarative Transaction Management

In a declarative transaction management approach, the developer will declare the transactional semantics in a configuration file rather than programming it using code. The main advantage with this is instead of writing the logic to manage, your application classes are free from transaction related logic and if you want to switch or don’t want to use transaction you don’t need to modify your logic.

As transactions is a cross-cutting functionality, to implement transactionality we use declarative AOP configuration. In order to work with AOP based transactions, we need to import AOP and Tx Namespaces.

We begin and commit or rollback transactions on an operation, we will begin the transaction while entering a method and while returning from the method we will commit and if the method throws exception we will roll back the transaction. So, if you observe it is the combination of Around and Throws advice.

In order to manage it we need to declare an transactional advice which will manages the above said logic. In order to configure an transactional advice we need to use Tx namespace to declare it as shown below.

<tx:advice id=”txAdvice” transaction-manager=”transactionManager”>
     <tx:attributes>
           <tx:method name=”insert*” read-only=”false”/>
     </tx:attributes>
</tx:advice>

The transaction advice will issue a commit or rollback on the transaction manager, so it needs a transaction manager to perform this, to declare a transaction manager; you need to pass the datasource as a reference to manage all the connections created by the datasource. read-only = “false” indicates you are performing an updatable operation. If you are performing only select type of operation you need to use read-only=”true”. Along with that you can specify the propagation attribute as well.

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

Once the advice has been declared, you need to declare the classes on which you want to apply this advice using the tag as shown below.

<app:config>
     <aop:pointcut expression=”execution(* com.dtx.service.*.*(..))” id=”txpc”/>
     <aop:advisor advice-ref=”txAdvice” pointcut-ref=”txpc”/>
</app:config>

Typical Spring Project Flow

In the below example we are trying to insert a Student, inserting a student involves inserting into Student information into student table as well as inserting into student course table. These two insertions has to be performed as part of single transactions. So the service will perform operations by using two dao’s and the transaction is imposed on Service class.

EducationalController.java

import com.dtx.service.RamuEducationalService;

public class EducationalController {
       private RamuEducationalService ramuEducationalService;
        
       public void insert(int studentId, String name, int courseId) {
              int outcome = 0;
                           
              outcome = ramuEducationalService.insert(studentId, name, courseId);
              if (outcome > 0) {
                     System.out.println(Student Inserted Successfully);
              }
       }
     
        public void setRamuEducationalService(RamuEducationalService ramuEducationalService) {
                this.ramuEducationalService = ramuEducationalService;
        }
}

RamuEducationalService.java

public interface RamuEducationalService {
       public int insert(int studentId, String name, int courseId);
}


RamuEducationalServiceImpl.java

import org.springframework.transaction.annotation.Transactional;

import com.dtx.dao.StudentCourseDao;
import com.dtx.dao.StudentDao;

public class RamuEducationalServiceImpl implements RamuEducationalService {
       private StudentDao studentDao;
       private StudentCourseDao studentCourseDao;
           
       @Override
       public int insert(int studentId, String name, int courseId) {
              int outcome = 0;
              outcome = studentDao.insert(studentId, name);
              if (outcome > 0) {
                     // re-initialize
                     outcome = 0;
                     outcome = studentCourseDao.insert(studentId, courseId);
              }
               return outcome;
       }
       
        public void setStudentDao(StudentDao studentDao) {
               this.studentDao = studentDao;
        }
        public void setStudentCourseDao(StudentCourseDao studentCourseDao) {
               this.studentCourseDao = studentCourseDao;
        }       
}

StudentDao.java

public interface StudentDao {
       public int insert(int studentId, String name);
}

StudentDaoImpl.java

import org.springframework.jdbc.core.JdbcTemplate;

public class StudentDaoImpl implements StudentDao {
       private final String SQL_INSERT_STUDENT = INSERT INTO STUDENT(STUDENT_ID, NAME) VALUES(?,?);
       private JdbcTemplate jdbcTemplate;
      
       public StudentDaoImpl(JdbcTemplate jdbcTemplate) {
              this.jdbcTemplate = jdbcTemplate;
       }
        @Override
        public int insert(int studentId, String name) {
               return jdbcTemplate.update(SQL_INSERT_STUDENT, new Object[] { studentId, name });
        }
}

StudentCourseDao.java

public interface StudentCourseDao {
       public int insert(int studentId, int courseId);
}

StudentCourseDaoImpl.java

import org.springframework.jdbc.core.JdbcTemplate;

public class StudentCourseDaoImpl implements StudentCourseDao {
       private final String SQL_INSERT_STUDENTCOURSE = INSERT INTO STUDENTCOURSE(STUDENT_ID, COURSE_ID) VALUES(?,?);
       private JdbcTemplate jdbcTemplate;
       
       public StudentCourseDaoImpl(JdbcTemplate jdbcTemplate) {
              this.jdbcTemplate = jdbcTemplate;
       }
        @Override
        public int insert(int studentId, int courseId) {
               return jdbcTemplate.update(SQL_INSERT_STUDENTCOURSE, new Object[]{studentId, courseId});
        }
}


application-context.xml

<bean id=educationalController class=com.dtx.controller.EducationalController>
      <property name=ramuEducationalService ref=ramuEducationalService/>
</bean>

<bean id="ramuEducationalService"
class="com.dtx.service.RamuEducationalServiceImpl">
       <property name="studentDao" ref="studentDao"/>
       <property name="studentCourseDao" ref="studentCourseDao"/>
</bean>

<bean id=dataSource
class=org.springframework.jdbc.datasource.DriverManagerDataSource>
     <property name=driverClassName value=oracle.jdbc.driver.OracleDriver/>
     <property name=url value=jdbc:oracle:thin:@//localhost:1512/xe”/>
     <property name=username value=hr/>
     <property name=password value=hr/>
</bean>

<bean id=jdbcTemplate class=org.springframework.jdbc.core.JdbcTemplate>
      <constructor-arg ref=dataSource/>
</bean>

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

<bean id=studentDao class=com.dtx.dao.StudentDaoImpl>
      <constructor-arg ref=jdbcTemplate/>
</bean>

<bean id=studentCourseDao class=com.dtx.dao.StudentCourseDaoImpl>
      <constructor-arg ref=jdbcTemplate/>
</bean>

<tx:advice id=txAdvice transaction-manager=transactionManager>
      <tx:attributes>
           <tx:method name=insert* read-only=false/>
      </tx:attributes>
</tx:advice>

<aop:config>
     <aop:pointcut expression=execution(* com.dtx.service.*.*(..)) id=txpc/>
     <aop:advisor advice-ref=txAdvice pointcut-ref=txpc/>
</aop:config>

Annotation approach Transaction Management

In this instead of declaring the transaction semantics in the declaration file we will directly annotate the class methods with @Transactional annotation. You can specify the readOnly and propagation attributes in it.

In the earlier example instead of declaring the transactional semantics in the declaration, you can directly mark the service class methods with related to transaction annotation as shown below.

RamuEducationalServiceImpl.java

import org.springframework.transaction.annotation.Transactional;

import com.dtx.dao.StudentCourseDao;
import com.dtx.dao.StudentDao;

public class RamuEducationalServiceImpl implements RamuEducationalService {
       private StudentDao studentDao;
       private StudentCourseDao studentCourseDao;
       
       @Override
       @Transactional(readOnly=false)
       public int insert(int studentId, String name, int courseId) {
              int outcome = 0;
              outcome = studentDao.insert(studentId, name);
              if (outcome > 0) {
                      //re-initialize
                       outcome = 0;
                       outcome = studentCourseDao.insert(studentId, courseId);
              }
                return outcome;
       }
          
        public void setStudentDao(StudentDao studentDao) { 
               this.studentDao = studentDao;
       }
             
        public void setStudentCourseDao(StudentCourseDao studentCourseDao) {
               this.studentCourseDao = studentCourseDao;
       }
}

In order to detect your annotations that has been marked at class level, you need to declare a tag in spring beans configuration file as



Thanks for reading. If you like this post please follow us for more updates about technology related updates.

No comments: