Wednesday, November 2, 2016

Transaction Management in Spring



Transaction Management is one of the Best Features in Spring Framework now i am going to demonstrate in the following posts as Simple Concepts with Example Codes.

1. Introduction to Spring Transaction Management               

2. Transaction Models in Spring

3. Transaction Propagation in Spring

4. Examples on Spring Transaction Management(using xml & annotations)

5. Example on Spring Transaction Management with Hibernate and Mysql(using Annotations)


Introduction to Spring Transaction Management

"The Process Of Combining Related Operations into Single Unit and Execute Them in 'Do Every Thing or Nothing' Manner Called 'Transaction Management'".

Transaction Management have 3 Phases..

  •  Start Transaction.
  •  Process Transaction.
  •  Commit or Rollback Transaction.
As shown in the above Diagram...  Based on the Instruction coming from "Transactional Component" the 'TransactionManager' commit or rollback the application data into Database(resource) through 'Resource Manager'.

NOTE: The Application in which Transaction Management is enabled is known as 'Transactional Component or Transactional App'.

Types of Transactions
:

Based on The Number of resources(Database) involved Transactions are Categorized into 2 Types

1. Local Transactions.
2. Global or Distributed Transactions.

If there is only one Resource(Database) Involved in Transaction Then That Transaction is Called Local Transaction.
Ex: Transferring Money Between Same Bank Accounts.

If There are Multiple Types of Resources(Databases) or Number of Same Type Resources(Databases) Involved in Transaction that Transaction is Called Global or Distributed Transaction.
Ex: Transferring Money Between Different Bank Accounts.

Global or Distributed Transaction Works By Using "2PC(2 Phase Commit.)" Protocol,
There are 2 Levels in This Protocol

   In 1st level The Distributed Transaction Manager seeks Permission  
   from the resources(Databases) to commit the Transaction.

   In 2nd Level if all the resources gives Permission to Commit then
   the Distributed Transaction will be Committed otherwise it will be
   rolled back.

The flow of 2PC Protocol

https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFK6bhoKdTQLCDXYwZ_uZotpatlzTU_QeLDBzG_3AyLsb8yPBwUwESEK0noNE7La0hDyRKdd6qOhPnLCJ_lx_qNUB_qCynGjiHxRjcpP0l6r9in1FSHl3_rVf3VHsIr_GgV8sU6PUkWRs/s1600/fig-two-phase-commit-overview.png


SpringEJB and Hibernate Supports Local Transactions.
Only Spring and EJB supports Global or Distributed Transactions.



Transaction Models in Spring:

There are 2 Types of Transaction Models

 1. Flat Transactions.                                                            

 2. Nested Transactions.

Flat Transaction is One Which Treats all operations of a transaction as one transaction,and if one operations fails then the entire Transaction will be Rolled back.

Transaction {

Operation 1

Operation 2

Operation 3

     .
     .

}

If any one of the above operations failed then the entire transaction will be rolled back.

Nested Transaction is one which Considers each operation in Transaction as sub Transaction of that Main Transaction.If any one of the Sub Transaction(operation) fails no impact on the Main Transaction.

Main Transaction{

sub transaction 1{
Operation 1
}

sub transaction 2{
Operation 2
}

sub transaction 3{
Operation 3
}

     .
     .

}

If any one of the above sub Transaction failed no Effect on Main Transaction.

NOTE: SpringEJB and Hibernate Supports Flat Transactions.
       Only Spring supports Nested Transactions.  



Transaction Propagation in Spring:

   "Making a Business Method working with client supplied Transaction

    is called 'Transaction Propagation'."

There are 6 types of "Transaction Propagation" attributes..
                                                                                                 

1. REQUIRED

2. REQUIRES NEW

3. SUPPORTS

4. NOT SUPPORTED

5. MANDATORY

6. NEVER

REQUIRED: If Client Calls Service(Business) Method having this attribute with Transaction then Service Method runs with That Transaction else Service Method runs with new Transaction.This is Most Popularly used Attributed.

REQUIRES NEW: Service Method having this attribute Always Runs with new Transaction Even though  the Client calls with Transaction or without Transaction.

SUPPORTS: Service Method having this attribute
 runs with Transaction if Client Calls with Transaction.
 runs with out Transaction if Client Calls with out Transaction.

NOT SUPPORTED: Service Method having this attribute Always Runs with out Transaction Even though  the Client calls with Transaction or without Transaction.

MANDATORY: The Client Should always call Service Method having this attribute with Transaction otherwise Exception will be raised.

NEVER: The Client Should always call Service Method having this attribute with out Transaction otherwise Exception will be raised.

NOTE: The Programmatic Transaction Management approach Does not support to this Transaction Propagation where Declarative Transaction Management Supports.


Examples on Spring Transaction Management (using xml & annotations):


In Spring There are 2 ways to use Transaction Management

          1. Programmatic.                                    
          2. Declarative.

Programmatic Approach is outdated now so I don't want to discuss it. So we move to Declarative Approach. In Declarative Approach There are 2 ways...

      Using Xml Configurations.
      Using Annotations.

Let me use 'Xml Configurations' in my following Eg. Application.

Example on Spring Transaction Management using 'Xml Configurations':

In My Example i am taking

 Demo(I)
 DemoBean(C)
 Spring.cfg.xml(Spring configuration file)
 DemoClient(Class To Run Application)
 and mysql database.

Demo:
package p1;

public interface Demo {
 public void bm1();
}

DemoBean:
package p1;

import org.springframework.jdbc.core.JdbcTemplate;
public class DemoBean implements Demo{
 private JdbcTemplate jt;

 /**
  * @param jt the jt to set
  */
 public void setJt(JdbcTemplate jt) {
  this.jt = jt;
 }

 @Override
 public void bm1() {
  int r1=jt.update("insert into Employee (eid,name,salary) values(2012,'Arjun1',100000)");
  int r2=jt.update("UPDATE Employee set salary=50000 where eid=2010");
 
  if(r1==0 || r2==0){
   System.out.println("Transaction is Rollbacked.");
   throw new RuntimeException();
   }else{
   System.out.println("Tx is Committed.");
  }
 }
}

DemoClient:
package p1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class DemoClient {

 public static void main(String s[]){
 
  /*FileSystemResource res=new FileSystemResource("Spring.cfg.xml");
  XmlBeanFactory factory=new XmlBeanFactory(res);
  Demo d=(Demo)factory.getBean("tfb");
  d.bm1();*/
  ApplicationContext ctx=new FileSystemXmlApplicationContext("Spring.cfg.xml");
  Demo d=(Demo) ctx.getBean("tfb");
  d.bm1();
 }
}

Note: You can use Either BeanFactory or ApplicationContext container.

Spring.cfg.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
 ....">

 <bean id="dmds"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
 <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://localhost:3306/database name"/>
 <property name="username" value="username"/>
 <property name="password" value="password"></property>
 </bean>

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

 <bean id="jt1" class="org.springframework.jdbc.core.JdbcTemplate">
     <property name="dataSource" ref="dmds"/>
 </bean>
    <bean id="db" class="p1.DemoBean">
        <property name="jt" ref="jt1"/>
    </bean>

    <!-- Pointcut Advisor to apply Transaction Attributes. -->
    <bean id="tas" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
        <property name="properties">
            <props>
                <prop key="bm1">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>

     <!-- Gives Proxy Object having Transaction Service. -->
    <bean id="tfb" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target" ref="db"/>
        <property name="transactionManager" ref="dts"/>
        <property name="transactionAttributeSource" ref="tas"/>
    </bean>
 </beans>

Run it from DemoClient.

Example on Spring Transaction Management using  'Annotations':
In this Example I take..

 Demo(I)
 DemoBean(C)
 Spring.cfg.xml(Spring configuration file)
 DemoClient(Class To Run Application)
 and mysql database.

all are same as above Application Except these Changes

in DemoBean add @Transactional annotation on top of bm1() as shown below

 @Transactional(propagation=Propagation.REQUIRED)
   public void bm1()
  {.....
  .....
  ...}

2. The Spring Configuration file is as Follows
<beans....>
  <bean id="drds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/databasename"/>
        <property name="username" value="username"/>
        <property name="password" value="password"/>
    </bean>

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

    <bean id="jt1" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="drds"/>
    </bean>

    <bean id="db" class="p1.DemoBean">
        <property name="jt" ref="jt1"/>
    </bean>

    <tx:annotation-driven transaction-manager="txmgr"/><!-- Enables Annotation Based Transaction Management. -->
</beans>


Run it with these Changes.


Example on Spring Transaction Management with Hibernate and Mysql(using Annotations):

In this post i would like to discuss about Spring Tx.Magmt with Annotations with "Spring + Hibernate + Mysql "
In this Example i take

Demo(I)                                                                                  

DemoBean(c)
DemoClient(c)
Spring.xml(Spring configuration file.)
hibernate.cfg.xml(Hibernate configuration file.)
Employee.hbm.xml(Hibernate Mapping File.)

Demo:
package p1;

public interface Demo {
 public void bm1()throws RuntimeException,Exception;
}

DemoBean: 
package p1;

public class DemoBean implements Demo{
 private SessionFactory sf;

 /**
  * @param sf the sf to set
  */
 public void setSf(SessionFactory sf) {
  this.sf = sf;
 }

 @Override
 @Transactional(propagation=Propagation.REQUIRED)
 public void bm1()throws Exception {
 
  Session session=sf.getCurrentSession();
  int r1=session.createQuery("UPDATE Employee set salary=1 where sno=6").executeUpdate();
  int r2=session.createQuery("UPDATE Employee set name='kkk' where sno=57").executeUpdate();

  if(r1==0 || r2==0){
   System.out.println("Tx is rolling back.");
   throw new RuntimeException();
  }else{
   System.out.println("Tx is Committed");
  }
 }
}

Spring.xml:
<beans...
     .....>
    <tx:annotation-driven transaction-manager="hbt"/>

    <bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
             <property name="configLocation" value="hibernate.cfg.xml"/>
             <property name="dataSource" ref="drds" />
    </bean>

    <bean id="drds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
        <property name="username" value="username"/>
        <property name="password" value="password"/>
    </bean>

    <bean id="hbt" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>
   
    <bean id="db" class="p1.DemoBean">
         <property name="sf" ref="mySessionFactory"/>
     </bean>
</beans>

hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory>
  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
 
  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/databasename</property>
  <property name="hibernate.connection.username">username</property>
<property name="hibernate.connection.password">password</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
  <property name="hibernate.connection.autocommit">false</property>
  <property name="hibernate.show_sql">true</property>
 <mapping resource="Employee.hbm.xml" />
 </session-factory>
</hibernate-configuration>

Employee.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="p1.Employee" table="Employee" catalog="database name">
        <id name="sno" type="java.lang.Integer">
            <column name="sno" />
            <generator class="identity" />
        </id>
        <property name="name" type="string">
            <column name="name" length="30" not-null="true" />
        </property>
        <property name="salary" type="float">
            <column name="salary" precision="8" not-null="true" />
        </property>
        <property name="eid" type="int">
            <column name="eid" not-null="true" />
        </property>
    </class>
</hibernate-mapping>


DemoClient:
package p1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoClient {
 public static void main(String s[]){
 ApplicationContext ctx=new ClassPathXmlApplicationContext("Spring.cfg.xml");
  Demo d=(Demo)ctx.getBean("db");
  try {
   d.bm1();
  } catch (RuntimeException e) {
   e.printStackTrace();
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

Run it.




Share:

0 comments:

Post a Comment