Transaction management is critical in any form of applications that will interact with the database. The application has to ensure that the data is consistent and the integrity of the data is maintained. There are many popular data frameworks like JDBC, JPA, Hibernate etc.. and Spring Framework provides a seamless way of integrating with these frameworks. In this article, we will see how Spring Framework leverages the transaction management capabilities with a plenty of examples. This article covers Programmatic as well as Declarative way of managing transactions. The final section of the article is about achieving transaction management through annotations and AOP. The readers of this article are assumed to have a basic understanding on core Spring Framework. If you don’t have the basic knowledge on the Spring Framework, please read the articles listed in the following section.
also read:
Transaction Management in Spring Framework Example Code
- [download id=”4″]
Transaction Management without Spring
In this section we will see how an application tends to manage transactions. We will develop an application which will provide a service to book a movie ticket. The domain objects User, Account and MovieTicket are involved. An User can book a Movie ticket in which case the amount will be debited from his/her Account.
Account
Account.java
package net.javabeat.spring.articles.txn; public class Account { private String id; private double amount; public String getId() { return id; } public void setId(String id) { this.id = id; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } }
The above code listing is for the declaration of Account object which declares the properties id and amount. Given below is the declaration of the movie ticket object. Note that this class contains the name of the movie and the total number of tickets at any point of time.
Movie Ticket
MovieTicket.java
package net.javabeat.spring.articles.txn; public class MovieTicket { private String id; private String movieName; private int totalTicketsCount; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getMovieName() { return movieName; } public void setMovieName(String movieName) { this.movieName = movieName; } public int getTotalTicketsCount() { return totalTicketsCount; } public void setTotalTicketsCount(int totalTicketsCount) { this.totalTicketsCount = totalTicketsCount; } }
The User object contains the id of an user as well as the user name. Note that this class also contains a reference to the Account object. When a movie ticket is booked, the ticketId property of the User class will be updated with the Id of the MovieTicket object.
User
User.java
package net.javabeat.spring.articles.txn; public class User { private String id; private String name; private Account account; private String ticketId; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public String getTicketId() { return ticketId; } public void setTicketId(String ticketId) { this.ticketId = ticketId; } }
Booking Service
Given below is the BookingService interface that will be exposed to the clients and will be used for booking the ticket through ‘bookTicket’ method.
BookingService.java
package net.javabeat.spring.articles.txn; public interface BookingService { void bookTicket(int userId, int ticketId, int noOfTickets); }
Utils class
As we proceed further in this article, we will be seeing plenty of classes using some common methods and so we will have the following Utility classes that will provide common methods for loading the application context and returning an instance of Jdbc Template.
Utils.java
package net.javabeat.spring.articles.txn; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; public class Utils { static ApplicationContext context = null; public static JdbcTemplate jdbcTempalte(){ initContext(); DriverManagerDataSource dataSource = (DriverManagerDataSource)context.getBean("mySqlDataSource"); System.out.println("Data source is " + dataSource); JdbcTemplate template = new JdbcTemplate(dataSource); return template; } public static ApplicationContext getContext(){ initContext(); return context; } public static ApplicationContext getContext(String configFilename){ return new ClassPathXmlApplicationContext(configFilename); } private static void initContext(){ if (context == null){ context = new ClassPathXmlApplicationContext("main.xml"); } } }
Configuration file
Note that the above Utility class provides method for loading the application from Spring’s configuration file and the contents of the file are given below.
</pre> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="mySqlDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/test"/> <property name="username" value="root"/> <property name="password" value="***"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="mySqlDataSource" /> </bean> <bean id="bookingTransactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="purchase">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="ticketBookingProxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="ticketBookingServiceThroughAop" /> <property name="interceptorNames"> <list> <value>bookingTransactionInterceptor</value> </list> </property> </bean> <bean id = "ticketBookingServiceThroughAop" class = "net.javabeat.spring.articles.txn.ticbook.aop.TicketBookingServiceThroughAop"> <property name="dataSource" ref="mySqlDataSource" /> </bean> </beans> <pre>
Ticket Utils
The following TicketUtils class contains the various methods that are applicable while booking a movie ticket and that will be reused across various classes that we will be seeing in the later section of this article.
TicketUtils.java
package net.javabeat.spring.articles.txn; import java.util.Iterator; import java.util.List; import org.apache.commons.collections.map.ListOrderedMap; import org.springframework.jdbc.core.JdbcTemplate; public class TicketUtils { @SuppressWarnings("unchecked") public static int getAccountId(JdbcTemplate template, int userId){ String sql = "select * from user where id = '" + userId + "'"; List<Object> userList= template.queryForList(sql); int accountId = -1; for (Object userObject : userList){ if (userObject instanceof ListOrderedMap){ ListOrderedMap map = (ListOrderedMap)userObject; Iterator<Object> iterator = map.keySet().iterator(); while (iterator.hasNext()){ Object key = iterator.next(); Object value = map.get(key); if (key.equals("account_id")){ accountId = Integer.parseInt(value.toString()); } System.out.println("Key - " + key + ", Value - " + value); } } } return accountId; } @SuppressWarnings("unchecked") public static float findTicketCost(JdbcTemplate template, int ticketId){ String sql = "select * from movie_ticket where id = '" + ticketId + "'"; List<Object> userList= template.queryForList(sql); float ticketCost = 0; for (Object userObject : userList){ if (userObject instanceof ListOrderedMap){ ListOrderedMap map = (ListOrderedMap)userObject; Iterator<Object> iterator = map.keySet().iterator(); while (iterator.hasNext()){ Object key = iterator.next(); Object value = map.get(key); if (key.equals("price")){ ticketCost = Float.parseFloat(value.toString()); } System.out.println("Key - " + key + ", Value - " + value); } } } return ticketCost; } public static void deductMoneyFromAccount(JdbcTemplate template, int accountId, float amount){ String sql = "update account set amount = (amount - " + amount + ") where id = " + accountId + ""; template.execute(sql); } public static void reduceTicketCount(JdbcTemplate template, int ticketId, int noOfTickets){ String sql = "update movie_ticket set tickets_count = (tickets_count - " + noOfTickets + ") where id = " + ticketId + ""; template.execute(sql); } public static int balanceMoney(JdbcTemplate template, int accountId){ String sql = "select amount from account where id = '" + accountId + "'"; return template.queryForInt(sql); } }
Note that all the methods are qualified with public and static qualifiers and they accept an instance of JdbcTemplate object so that any class can directly access the methods by passing in an instance of JdbcTemplate object. We will see the functionality of the methods in this section in brief.
The bookTicket() method of the TicketBookingService accepts userId as one of the arguments and the rest being the ticketId and the number of tickets to be booked. So the first step is to identify the accountId of the corresponding user and the method getAccountId just does that. It takes the user object from the database for the corresponding userId and returns the accountId of the user. Then the cost of the method has to be found so that the total price of the ticket can be calculated by considering the number of tickets to be booked for the movie. So the method findTicketCost() takes the ticketId as an argument and returns the cost of the ticket.
Once the total cost is calculated, the money has to be deducted from the user’s account. We already have accountId of the user and deduction of the money happens through the deductMoneyFromAccount() method. Finally, in the case of successful booking, the number of tickets will be deducted from the table representing Movie Ticket.
Ticket Booking Service
Here is the concrete implementation of the Ticket Spring without any framework support. The core of the class lies in the bookTicket() method where it uses JdbcTemplate to achieve the operation. By default, the auto-commit setting for a Connection object is set to true, which means that for every insert/update/delete operation, the database will be committed. However, in the case of our example, deducting money from the bank account, deducing the number of tickets (which are the updatable operations) should commit to the database only when the booking is successful, and in the case the failure, the application has to make sure that the transaction is made to roll-back.
TicketBookingService.java
package net.javabeat.spring.articles.txn.ticbook; import java.sql.Connection; import net.javabeat.spring.articles.txn.TicketUtils; import org.springframework.jdbc.core.JdbcTemplate; public class TicketBookingService { private JdbcTemplate template; public void setTemplate(JdbcTemplate template){ this.template = template; } public void bookTicket(int userId, int ticketId, int noOfTickets){ Connection connection = null; try{ connection = template.getDataSource().getConnection(); connection.setAutoCommit(false); int accountId = TicketUtils.getAccountId(template, userId); float ticketCost = TicketUtils.findTicketCost(template, ticketId); float totalCost = (noOfTickets * ticketCost); TicketUtils.deductMoneyFromAccount(template, accountId, totalCost); TicketUtils.reduceTicketCount(template, ticketId, noOfTickets); connection.commit(); }catch (Exception exception){ exception.printStackTrace(); if (connection != null){ try{ connection.rollback(); }catch (Exception e1){ e1.printStackTrace(); } } } } }
Note that before the start of the method, the auto-commit of the Connection is set to false. In the case of failure, in which case an exception will be thrown, the transaction is made to roll-back by calling the rollback() method on the Connection object. In the case of successful booking, the method commit() is called on the Connection indicating that the transaction is successful.
Client Application
Main.java
package net.javabeat.spring.articles.txn.ticbook; import net.javabeat.spring.articles.txn.Utils; import org.springframework.jdbc.core.JdbcTemplate; public class Main { public static void main(String[] args) { JdbcTemplate template = Utils.jdbcTempalte(); TicketBookingService service = new TicketBookingService(); service.setTemplate(template); service.bookTicket(1, 1, 1); } }
The client application invokes the ticket booking by calling bookTicket() method on the TicketBookingService object. The arguments passed to the method are user id, ticket id and the number of tickets to be booked.
Programmatic Transaction Management – Transaction Manager
In the previous section, we saw how transaction got maintained by the application, in which case application has to manually do a commit or a rollback in the case of a successful or a failure transactions. In the forth coming sections of the article, we will see how to manage transactions using Spring framework. Spring provides support for both Programmatic and Declarative mode of transaction management, although most of the applications prefer using Declarative mode of transaction management since the transaction related code is completed shielded from the application code. The advantage in having Programmatic mode of transaction management is that the application will have greater control over the transaction.
Ticket Booking Service
TicketBookingServiceThroughPlatformManager.java
package net.javabeat.spring.articles.txn.ticbook.pm; import net.javabeat.spring.articles.txn.TicketUtils; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; public class TicketBookingServiceThroughPlatformManager extends JdbcDaoSupport{ private PlatformTransactionManager transactionManager; public void setTransactionManager(PlatformTransactionManager transactionManager){ this.transactionManager = transactionManager; } public void bookTicket(int userId, int ticketId, int noOfTickets){ TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(transactionDefinition); try{ int accountId = TicketUtils.getAccountId(getJdbcTemplate(), userId); float ticketCost = TicketUtils.findTicketCost(getJdbcTemplate(), ticketId); float totalCost = (noOfTickets * ticketCost); TicketUtils.deductMoneyFromAccount(getJdbcTemplate(), accountId, totalCost); TicketUtils.reduceTicketCount(getJdbcTemplate(), ticketId, noOfTickets); transactionManager.commit(status); }catch (Exception e){ transactionManager.rollback(status); } } }
In the above code listing, we have used Platform Transaction Manager for performing programmatic mode of transaction management. The Platform Transaction Manager is the core API in Spring framework for transaction management. This class completely shields the internals of Transaction management so that the application can concentrate only on the business logic. The Platform Transaction Manager class is also wrapped with the Data Source that connects to the database. Closely related objects with the Platform Transaction Manager are Transaction Definition and Transaction Status objects.
The Transaction Definition object encapsulates the transactional properties such as the isolation level, propagation behavior, timeout etc. The Transaction Status object represents the status of the executing transaction whether it is in New status, completed status etc. In the beginning of the method, we acquire a Transaction Status object from the Transaction Definition object. In the case of successful scenario, the transaction is commited on the Transaction Status object, whereas in the case of failure, the transaction is made to roll-back.
Client Application
Main.java
package net.javabeat.spring.articles.txn.ticbook.pm; import javax.sql.DataSource; import net.javabeat.spring.articles.txn.Utils; import org.springframework.context.ApplicationContext; import org.springframework.transaction.PlatformTransactionManager; public class Main { public static void main(String[] args) { ApplicationContext context = Utils.getContext(); TicketBookingServiceThroughPlatformManager service = new TicketBookingServiceThroughPlatformManager(); DataSource dataSource = (DataSource)context.getBean("mySqlDataSource"); service.setDataSource(dataSource); PlatformTransactionManager transactionManager = (PlatformTransactionManager)context.getBean("transactionManager"); service.setTransactionManager(transactionManager); service.bookTicket(1, 2, 3); } }
The client code creates an instance of the appropriate service object and before invoking the method bookTicket() it sets the appropriate data source and the transaction manager objects. The bean declaration for transactionManager is given below,
<bean id="transactionManager"> <property name="dataSource" ref="mySqlDataSource" /> </bean>
Programmatic Transaction Management – Transaction Template
In this section, we will look into an alternative way for programmatic management of the transactions through TransactionTemplate class. Like all other template classes in Spring, TransactionTemplate class simplifies the job of managing transactions and is useful for applications that intends to intercept low-level transactional details.
Ticket Booking Service
Note that the below class resembles the TicketBookingService that we saw in the last section, however there is no transactional related code (like commit and rollback).
TicketBookingServiceThroughTemplate.java
package net.javabeat.spring.articles.txn.ticbook.template; import net.javabeat.spring.articles.txn.TicketUtils; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; public class TicketBookingServiceThroughTemplate extends JdbcDaoSupport{ private PlatformTransactionManager transactionManager; public void setTransactionManager(PlatformTransactionManager transactionManager){ this.transactionManager = transactionManager; } public void bookTicket(int userId, int ticketId, int noOfTickets){ TransactionTemplate template = new TransactionTemplate(transactionManager); BookingTransactionCallback callback = new BookingTransactionCallback(this, userId, ticketId, noOfTickets); template.execute(callback); } }
The above class delgates the operation to the class BookingTransactionCallback which is discussed below.
Booking Transaction Callback
BookingTransactionCallback.java
public class BookingTransactionCallback extends TransactionCallbackWithoutResult{ private JdbcDaoSupport daoSupport; private int userId; private int ticketId; private int noOfTickets; public BookingTransactionCallback(JdbcDaoSupport daoSupport, int userId, int ticketId, int noOfTickets){ this.daoSupport = daoSupport; this.userId = userId; this.ticketId = ticketId; this.noOfTickets = noOfTickets; } @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { int accountId = TicketUtils.getAccountId(daoSupport.getJdbcTemplate(), userId); float ticketCost = TicketUtils.findTicketCost(daoSupport.getJdbcTemplate(), ticketId); float totalCost = (noOfTickets * ticketCost); TicketUtils.deductMoneyFromAccount(daoSupport.getJdbcTemplate(), accountId, totalCost); TicketUtils.reduceTicketCount(daoSupport.getJdbcTemplate(), ticketId, noOfTickets); } }
There are two flavors of TransactionTemplate classes, one that returns result and the other that doesn’t return result and they are TransactionCallback and TransactionCallbackWithoutResult. In our example, we won’t be returning any result and hence the callback extends TransactionCallbackWithoutResult.
Client Application
Main.java
package net.javabeat.spring.articles.txn.ticbook.template; import javax.sql.DataSource; import net.javabeat.spring.articles.txn.Utils; import org.springframework.context.ApplicationContext; import org.springframework.transaction.PlatformTransactionManager; public class Main { public static void main(String[] args) { ApplicationContext context = Utils.getContext(); TicketBookingServiceThroughTemplate service = new TicketBookingServiceThroughTemplate(); DataSource dataSource = (DataSource)context.getBean("mySqlDataSource"); service.setDataSource(dataSource); PlatformTransactionManager transactionManager = (PlatformTransactionManager)context.getBean("transactionManager"); service.setTransactionManager(transactionManager); service.bookTicket(1, 1, 2); } }
The client application loads the context for getting a reference to transaction Manager and data source objects which is given below,
<bean id="mySqlDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/test"/> <property name="username" value="root"/> <property name="password" value="mother"/> </bean> <bean id="transactionManager"> <property name="dataSource" ref="mySqlDataSource" /> </bean>
Transaction Management through Annotations
In this section, we will see how to manage transactions declaratively using Spring’s annotations
Ticket Booking Service
TicketBookingServiceThroughAnnotation.java
package net.javabeat.spring.articles.txn.ticbook.annotation; import net.javabeat.spring.articles.txn.BookingService; import net.javabeat.spring.articles.txn.TicketUtils; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.transaction.annotation.Transactional; public class TicketBookingServiceThroughAnnotation extends JdbcDaoSupport implements BookingService{ @Transactional public void bookTicket(int userId, int ticketId, int noOfTickets){ int accountId = TicketUtils.getAccountId(getJdbcTemplate(), userId); float ticketCost = TicketUtils.findTicketCost(getJdbcTemplate(), ticketId); float totalCost = (noOfTickets * ticketCost); TicketUtils.deductMoneyFromAccount(getJdbcTemplate(), accountId, totalCost); TicketUtils.reduceTicketCount(getJdbcTemplate(), ticketId, noOfTickets); } }
The annotation @Transactional can be applied to a method or at the class level. If applied to a class, all the public methods within the class will inherit the @Transactional annotation. Note that it is also possible to configure the isolation level and the propagation nature. If not specified, the propagation defaults to REQUIRED and for the isolation level, it uses the database isolation level. Note that the above class extends JdbcDaoSupport and so while creating an instance, the jdbcTemplate object has to be set.
Client Application
Main.java
package net.javabeat.spring.articles.txn.ticbook.annotation; import net.javabeat.spring.articles.txn.BookingService; import net.javabeat.spring.articles.txn.Utils; import org.springframework.context.ApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = Utils.getContext("main-annotation.xml"); BookingService service = (BookingService)context.getBean("ticketBookingServiceThroughAnnotation"); service.bookTicket(1, 1, 1); } }
Note that the client application references the configuration file main-annotation.xml which is shown below.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="transactionManager"> <property name="dataSource" ref="mySqlDataSource" /> </bean> <bean id="mySqlDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/test"/> <property name="username" value="root"/> <property name="password" value="mother"/> </bean> <bean id = "ticketBookingServiceThroughAnnotation" class = "net.javabeat.spring.articles.txn.ticbook.annotation.TicketBookingServiceThroughAnnotation"> <property name="dataSource" ref="mySqlDataSource" /> </bean> </beans>
Note that for this annotation based approach to work, the element <tx:annotation-driven> has to be specified in the configuration file along with the property transactionManager in which case all the beans that are visible to this context will be considered and those beans which are annotated with @Transactional <strong>annotations</strong> will be intercepted for transactional processing.
Transaction Management through AOP
We will see the final way of declarative transaction managementprovided by Spring which is AOP based. AOP programming enables the concerns that cross-cut over the application can be separated and can be encapsulated. Since marking the beginning of a transaction, end of a transaction, commiting the database in the case of successful operation and making a rollback for failure operations are really cross-cutting concerns, it is ideal to take AOP based approach for this scenario.
Ticket Booking Service
TicketBookingServiceThroughAop.java
package net.javabeat.spring.articles.txn.ticbook.aop; import net.javabeat.spring.articles.txn.TicketUtils; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class TicketBookingServiceThroughAop extends JdbcDaoSupport{ public void bookTicket(int userId, int ticketId, int noOfTickets){ int accountId = TicketUtils.getAccountId(getJdbcTemplate(), userId); float ticketCost = TicketUtils.findTicketCost(getJdbcTemplate(), ticketId); float totalCost = (noOfTickets * ticketCost); TicketUtils.deductMoneyFromAccount(getJdbcTemplate(), accountId, totalCost); TicketUtils.reduceTicketCount(getJdbcTemplate(), ticketId, noOfTickets); } }
Note that there is literally no change in Ticket Booking Service class as the AOP related stuffs will be configured in an external configuration file.
Client Application
Main.java
package net.javabeat.spring.articles.txn.ticbook.aop; import net.javabeat.spring.articles.txn.Utils; import org.springframework.context.ApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = Utils.getContext(); TicketBookingServiceThroughAop service = (TicketBookingServiceThroughAop)context.getBean("ticketBookingServiceThroughAop"); service.bookTicket(1, 1, 1); } }
The client tries to load the bean ‘ticketBookingServiceThroughAop’ which will actually be a proxy over the real interface TicketBookingService. Have a look at the configuration which is given below.
<bean id="bookingTransactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="purchase">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="ticketBookingProxyService"> <property name="target" ref="ticketBookingServiceThroughAop" /> <property name="interceptorNames"> <list> <value>bookingTransactionInterceptor</value> </list> </property> </bean> <bean id = "ticketBookingServiceThroughAop" class = "net.javabeat.spring.articles.txn.ticbook.aop.TicketBookingServiceThroughAop"> <property name="dataSource" ref="mySqlDataSource" /> </bean>
Note that in the above declaration, we have declared the proxy bean ‘ticketBookingProxyService’ which is an instance of ‘org.springframework.aop.framework.ProxyFactoryBean’. The target for this proxy will be the Ticket Booking Service object which is specified through ‘target’ property. And because we want to intercept the transaction related operations, we have specified the interceptor ‘bookingTransactionInterceptor’ through the element ‘interceptorNames’.
also read:
Conclusion
In this article, we have seen how to integrate transaction related operations in Spring framework. We have also discussed the importance of transaction management and we saw how Spring provides the declarative and programmatic way of transaction management through plenty of code samples. Hope the readers attained the basic knowledge of doing transaction management in Springand know how to incorporate it in real-time applications.
If you have any doubts on the spring framework and transaction management, please post it in the comments section. Also search in our website to find lot of other interesting articles related to the spring framework. There are some interesting articles about spring framework, interview questions, spring and hibernate integration,etc. If you are looking for the detailed knowledge, buy any of the following books for the spring framework.