Spring Transaction Management Example JDBC

Filed Under: Spring

Spring Transaction Management is one of the most widely used and important feature of Spring framework. Transaction Management is a trivial task in any enterprise application. We have already learned how to use JDBC API for Transaction Management. Spring provides extensive support for transaction management and help developers to focus more on business logic rather than worrying about the integrity of data incase of any system failures.

Spring Transaction Management

spring transaction management, spring @Transactional, Spring JDBCTemplate, spring transaction

Some of the benefits of using Spring Transaction Management are:

  1. Support for Declarative Transaction Management. In this model, Spring uses AOP over the transactional methods to provide data integrity. This is the preferred approach and works in most of the cases.
  2. Support for most of the transaction APIs such as JDBC, Hibernate, JPA, JDO, JTA etc. All we need to do is use proper transaction manager implementation class. For example org.springframework.jdbc.datasource.DriverManagerDataSource for JDBC transaction management and org.springframework.orm.hibernate3.HibernateTransactionManager if we are using Hibernate as ORM tool.
  3. Support for programmatic transaction management by using TransactionTemplate or PlatformTransactionManager implementation.

Most of the features that we would want in a transaction manager is supported by Declarative transaction management, so we would use this approach for our example project.

Spring Transaction Management JDBC Example

We will create a simple Spring JDBC project where we will update multiple tables in a single transaction. The transaction should commit only when all the JDBC statements execute successfully otherwise it should rollback to avoid data inconsistency.

If you know JDBC transaction management, you might argue that we can get do it easily by setting auto-commit to false for the connection and based on the result of all the statements, either commit or rollback the transaction. Obviously we can do it, but that will result in a lot of boiler-plate code just for transaction management. Also the same code will present in all the places where we are looking for transaction management, causing tightly coupled and non-maintainable code.

Spring declarative transaction management addresses these concerns by using Aspect Oriented Programming to achieve loose coupling and avoid boiler-plate code in our application. Let’s see how Spring does it with a simple example.

Before we jump into our Spring project, let’s do some database setup for our use.

Spring Transaction Management – Database Setup

We will create two tables for our use and update both of them in a single transaction.

CREATE TABLE `Customer` (
  `id` int(11) unsigned NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)

CREATE TABLE `Address` (
  `id` int(11) unsigned NOT NULL,
  `address` varchar(20) DEFAULT NULL,
  `country` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)

We could define foreign-key relationship here from Address id column to Customer id column, but for simplicity I am not having any constraint defined here.

Our Database setup is ready for spring transaction management project, lets create a simple Spring Maven Project in the Spring Tool Suite. Our final project structure will look like below image.

Spring Transaction Management Example

Let’s look into each of the pieces one by one, together they will provide a simple spring transaction management example with JDBC.

Spring Transaction Management – Maven Dependencies

Since we are using JDBC API, we would have to include spring-jdbc dependency in our application. We would also need MySQL database driver to connect to mysql database, so we will include mysql-connector-java dependency too.

spring-tx artifact provides transaction management dependencies, usually it’s included automatically by STS but if it’s not then you need to include it too. You might see some other dependencies for logging and unit testing, however we will not be using any of them. Our final pom.xml file looks like below code.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">


		<!-- Generic properties -->

		<!-- Spring -->

		<!-- Logging -->

		<!-- Test -->


		<!-- Spring and Transactions -->

		<!-- Spring JDBC and MySQL Driver -->

		<!-- Logging with SLF4J & LogBack -->

		<!-- Test Artifacts -->


I have updated the Spring versions to the latest one as of today. Make sure MySQL database driver is compatible with your mysql installation.

Spring Transaction Management – Model Classes

We will create two Java Beans, Customer and Address that will map to our tables.

package com.journaldev.spring.jdbc.model;

public class Address {

	private int id;
	private String address;
	private String country;
	public int getId() {
		return id;
	public void setId(int id) {
		this.id = id;
	public String getAddress() {
		return address;
	public void setAddress(String address) {
		this.address = address;
	public String getCountry() {
		return country;
	public void setCountry(String country) {
		this.country = country;

package com.journaldev.spring.jdbc.model;

public class Customer {

	private int id;
	private String name;
	private Address address;
	public int getId() {
		return id;
	public void setId(int id) {
		this.id = id;
	public String getName() {
		return name;
	public void setName(String name) {
		this.name = name;
	public Address getAddress() {
		return address;
	public void setAddress(Address address) {
		this.address = address;

Notice that Customer bean has Address as one of it’s variables. When we will implement DAO for Customer, we will get data for both customer and address table and we will execute two separate insert queries for these tables and that’s why we need transaction management to avoid data inconsistency.

Spring Transaction Management – DAO Implementation

Let’s implement the DAO for Customer bean, for simplicity we will just have one method to insert record in both customer and address tables.

package com.journaldev.spring.jdbc.dao;

import com.journaldev.spring.jdbc.model.Customer;

public interface CustomerDAO {

	public void create(Customer customer);

package com.journaldev.spring.jdbc.dao;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.journaldev.spring.jdbc.model.Customer;

public class CustomerDAOImpl implements CustomerDAO {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;

	public void create(Customer customer) {
		String queryCustomer = "insert into Customer (id, name) values (?,?)";
		String queryAddress = "insert into Address (id, address,country) values (?,?,?)";

		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

		jdbcTemplate.update(queryCustomer, new Object[] { customer.getId(),
				customer.getName() });
		System.out.println("Inserted into Customer Table Successfully");
		jdbcTemplate.update(queryAddress, new Object[] { customer.getId(),
				customer.getAddress().getCountry() });
		System.out.println("Inserted into Address Table Successfully");


Notice that CustomerDAO implementation is not taking care of transaction management. This way we are achieving separation of concerns because sometimes we get DAO implementations from third party and we don’t have control on these classes.

Spring Declarative Transaction Management – Service

Let’s create a Customer Service that will use the CustomerDAO implementation and provide transaction management when inserting records in the customer and address tables in a single method.

package com.journaldev.spring.jdbc.service;

import com.journaldev.spring.jdbc.model.Customer;

public interface CustomerManager {

	public void createCustomer(Customer cust);

package com.journaldev.spring.jdbc.service;

import org.springframework.transaction.annotation.Transactional;

import com.journaldev.spring.jdbc.dao.CustomerDAO;
import com.journaldev.spring.jdbc.model.Customer;

public class CustomerManagerImpl implements CustomerManager {

	private CustomerDAO customerDAO;

	public void setCustomerDAO(CustomerDAO customerDAO) {
		this.customerDAO = customerDAO;

	public void createCustomer(Customer cust) {


If you notice the CustomerManager implementation, it’s just using CustomerDAO implementation to create the customer but provide declarative transaction management through annotating createCustomer() method with @Transactional annotation. That’s all we need to do in our code to get the benefits of Spring transaction management.

@Transactional annotation can be applied over methods as well as whole class. If you want all your methods to have transaction management features, you should annotate your class with this annotation. Read more about annotations at Java Annotations Tutorial.
The only part remaining is wiring spring beans to get spring transaction management example to work.

Spring Transaction Management – Bean Configuration

Create a Spring Bean Configuration file with name as “spring.xml”. We will use this in our test program to wire spring beans and execute our JDBC program to test transaction management.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:context="https://www.springframework.org/schema/context"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.0.xsd
		https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<!-- Enable Annotation based Declarative Transaction Management -->
	<tx:annotation-driven proxy-target-class="true"
		transaction-manager="transactionManager" />

	<!-- Creating TransactionManager Bean, since JDBC we are creating of type 
		DataSourceTransactionManager -->
	<bean id="transactionManager"
		<property name="dataSource" ref="dataSource" />
	<!-- MySQL DB DataSource -->
	<bean id="dataSource"

		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/TestDB" />
		<property name="username" value="pankaj" />
		<property name="password" value="pankaj123" />

	<bean id="customerDAO" class="com.journaldev.spring.jdbc.dao.CustomerDAOImpl">
		<property name="dataSource" ref="dataSource"></property>

	<bean id="customerManager" class="com.journaldev.spring.jdbc.service.CustomerManagerImpl">
		<property name="customerDAO" ref="customerDAO"></property>


Important points to note in the spring bean configuration file are:

  • tx:annotation-driven element is used to tell Spring context that we are using annotation based transaction management configuration. transaction-manager attribute is used to provide the transaction manager bean name. transaction-manager default value is transactionManager but I am still having it to avoid confusion. proxy-target-class attribute is used to tell Spring context to use class based proxies, without it you will get runtime exception with message such as Exception in thread “main” org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named ‘customerManager’ must be of type [com.journaldev.spring.jdbc.service.CustomerManagerImpl], but was actually of type [com.sun.proxy.$Proxy6]
  • Since we are using JDBC, we are creating transactionManager bean of type org.springframework.jdbc.datasource.DataSourceTransactionManager. This is very important and we should use proper transaction manager implementation class based on our transaction API use.
  • dataSource bean is used to create the DataSource object and we are required to provide the database configuration properties such as driverClassName, url, username and password. Change these values based on your local settings.
  • We are injecting dataSource into customerDAO bean. Similarly we are injecting customerDAO bean into customerManager bean definition.

Our setup is ready, let’s create a simple test class to test our transaction management implementation.

package com.journaldev.spring.jdbc.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.jdbc.model.Address;
import com.journaldev.spring.jdbc.model.Customer;
import com.journaldev.spring.jdbc.service.CustomerManager;
import com.journaldev.spring.jdbc.service.CustomerManagerImpl;

public class TransactionManagerMain {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(

		CustomerManager customerManager = ctx.getBean("customerManager",

		Customer cust = createDummyCustomer();


	private static Customer createDummyCustomer() {
		Customer customer = new Customer();
		Address address = new Address();
		// setting value more than 20 chars, so that SQLException occurs
		address.setAddress("Albany Dr, San Jose, CA 95129");
		return customer;


Notice that I am explicitly setting address column value too long so that we will get exception while inserting data into Address table.

Now when we run our test program, we get following output.

Mar 29, 2014 7:59:32 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Sat Mar 29 19:59:32 PDT 2014]; root of context hierarchy
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Inserted into Customer Table Successfully
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.support.SQLErrorCodesFactory <init>
INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
Exception in thread "main" org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into Address (id, address,country) values (?,?,?)]; Data truncation: Data too long for column 'address' at row 1; nested exception is com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1
	at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:100)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:658)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:907)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:968)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:978)
	at com.journaldev.spring.jdbc.dao.CustomerDAOImpl.create(CustomerDAOImpl.java:27)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl.createCustomer(CustomerManagerImpl.java:19)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$FastClassBySpringCGLIB$$84f71441.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$EnhancerBySpringCGLIB$$891ec7ac.createCustomer(<generated>)
	at com.journaldev.spring.jdbc.main.TransactionManagerMain.main(TransactionManagerMain.java:20)
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2939)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1623)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1715)
	at com.mysql.jdbc.Connection.execSQL(Connection.java:3249)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1268)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1541)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1455)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1440)
	at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:914)
	at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:907)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:642)
	... 16 more

Notice the log message says that data inserted into customer table successfully but exception thrown by MySQL database driver clearly says that value is too long for the address column. Now if you will check the Customer table, you won’t find any row there that means that transaction is rolled back completely.

If you are wondering where the transaction management magic is happening, look at the logs carefully and notice the AOP and Proxy classes created by Spring framework. Spring framework is using Around advice to generate a proxy class for CustomerManagerImpl and only committing the transaction if the method returns successfully. If there is any exception, it’s just rolling back the whole transaction. I would suggest you to read Spring AOP Example to learn more about Aspect Oriented Programming model.

That’s all for Spring Transaction Management Example, download the sample project from below link and play around with it to learn more.


  1. Sanjay says:

    I am getting below Error :
    any help is appreciated
    {“resourceType”:”OperationOutcome”,”issue”:[{“severity”:”error”,”code”:”exception”,”diagnostics”:”Could not open JDBC Connection for transaction; nested exception is java.lang.NullPointerException”}]}

  2. mayur Vinod Patil says:

    I’m having multiple dao calls in same class and all dao calls are interdependent on each others and I want to run all dao calls in same transaction?
    Transaction should rollback if any error happens.

  3. Nissi says:

    Simply Super !!!

  4. Akshay says:

    It will be good if you can provide both annotation & xml based configurations whenever you are explaining any spring related modules.

  5. Mahesh Katkam says:

    Could you please explain the spring boot + transaction with example ..

  6. Gajendra says:

    I regularly visit your blog and find your solutions accurate and really helpful.
    Thanks for this amazing post.

  7. Raju says:

    Good post buddy.

  8. Tuhin subhra Dey says:

    Hi Pankaj,
    My data source is like

    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/…

    For this what changes is required in transaction manager class ?


  9. vijaya says:


    even though i have commented the following line in spring.xml. If i check the logs getting the same like before.

    Creating TransactionManager Bean, since JDBC we are creating of type

  10. Bhavani Shankar Reddy says:

    Thank you so much, Very well explained.

  11. Anonymous says:

    Hello, I tried this. The transaction rolls back when an error occurred when the method is called from my test class but when it is called in my controller, it does not rollback. Where might have gone wrong?

    1. Pankaj says:

      Please check if your connect pool auto-commit is false or not?

  12. Anuj Rastogi says:

    Excellent post! I love it.

  13. Mansi Mehra says:

    Is this the maven project which you created ? Also, Please share the path to download Maven Dependencies.

    1. Pankaj says:

      you don’t need to download the maven dependencies, it will be downloaded automatically when you build the project.

  14. Rajaravikiran S says:

    Great!!!! That was a clear explanation

  15. Vinay Sharma says:

    Thanks Pankaj. Its a nice and simple article for someone new to Spring Transaction management. Keep up the good work and keep sharing .


  16. Savani says:

    Hello Sir,
    I follow your tutorials they’re great. But I need your help in creating the Spring MVC MongoDB Jasper integration example. Could you please create it?

  17. Sayali says:

    What if I want to allow to continue the transaction ? What I would need to change ?


  18. Sachin Parse says:

    Nice article..!

  19. Concord says:


    Things are clear, but when I comment the @Transcational annotation still the program executes without any error.

    1. Vinay Sharma says:

      Concord, @Transcational is not supposed to fix any errors/exceptions. In the example which Pankaj has shared. If he hasn’t put @Transactional then, you would have seen an entry in the Customer table but not in the Address table.

      But if he has give correct values in the SQL queries i.e. address field value of correct length, then the program would work without error with/without @Transactional .

      Hope this clears your doubt.

  20. Abhishek Jotshi says:

    Very nicely explained. Very helpful.

  21. Raghu Kasturi says:

    Why do we inject dataSource instead of jdbcTemplate in DAO Imlp?

  22. ateek khan says:

    Very well explained.

  23. karthik prabakaran says:

    Nice post

  24. Arjun says:

    Hi Pankaj,
    Lovely reading ur article as always…

    Just want to point out that,

    The rollback happens when the method throws type of RuntimeException.

  25. npk says:

    Pankaj, thanks a lot.
    I would like to get an example for concurrency.

  26. udit says:

    HI pankaj Nice blog but i think you have missed something in second point of
    Some of the benefits of using Spring Transaction Management are:

    I think for JDBC it should be org.springframework.jdbc.datasource.DataSourceTransactionManager not org.springframework.jdbc.datasource.DriverManagerDataSource.

  27. wisarut says:

    It good Exsample

  28. Meenakshi says:

    Is it possible to have to transaction management over two databases within in same method.

  29. Tatarao says:

    Very good explanation , thank you

  30. raghu says:

    Well demonistrated with a simple and easy to understand example

  31. Haribabu says:

    Thanks Pankaj… Well explanation.I got good knowledge of transaction management with this example…!!!

  32. sath says:

    thanks pankaj ..nice example

  33. Srinu says:

    Clear explanation, I am quite benefited by this article.Thank you so much

  34. Srinu says:

    Nice Article..clear explanation…Thank you.

  35. Bhaskerreddy says:

    Explanation is very clear,Thanks……..

  36. Manimaran says:

    Thanks for your useful explanation.

  37. Marc Cappelletti says:

    Great explanation and concrete project to download! Thank you.

  38. Geetanjali says:

    Hi ,

    transaction is not working. Im getting exception but customer values are storing into customer table.To make it work i added @Transaction(readonly = false, rollbackfor = Excption.class) to the CustomerDaoImpl class

    1. Rajeev Akotkar says:

      hi geetanjali ,could you pls share the code.I wanna have a try at your problem. my email id is:rajeev.akotkar@yahoo.in

    2. chandra says:

      Hi Gitanjali,

      You are right.

    3. chandra says:

      Not working for me anyway.

  39. balaji says:

    where did you use transactionmanager in the code?? @Transactional doesn’t help me to rollback first table data (first table saved the data and second table failed with “Data too long for column”)..

  40. balaji says:

    where did you use transactionmanager in the code? because after adding @Transactional on top of createCustomer() — for me it saves first table values and failed to save second table with “Data too long for column”. so, here I did not achieve transactional case at all 🙁 please help me

  41. Ravi says:

    Thanks for the post Pankaj, Just wanted to know, any idea how to handle transactions with both OpenJpa and Spring JdbcTemplate.
    To explain more, both openJpa’s entirymanager and Spring jdbcTemplate uses the same datasource created. Some transaction uses entitymanager and some uses jdbctemplate. There is need of transaction handle between them.
    Thanks in advance.!

  42. Thava says:

    Very good simple example but powerful enough to explain some complex ideas! Good work, and thanks for sharing!

  43. Vidita says:

    Very well explained ! Really helps. Specially the last few lines which explains where the magic happens

  44. Piyush says:

    Very well explained.

  45. Sasi says:

    Excellent explanation

  46. Pankaj says:

    Well explained in simple language.

  47. Krish says:

    Thanks Pankaj for your reply.I was able to find the issue.

    Thanks again.

  48. Krish says:

    Thank you for the post.I was trying to implement your code.When I try to update the dependencies in pom.xml in Spring STS I get a Maven dependency problem.Not sure what the issue is.Is it related to the order in which the dependencies need to be added to the file?

    Thanks again for your help.

    1. Pankaj says:

      You can download the project and check the dependencies version. Order of dependencies doesn’t matter.

  49. Binh Thanh Nguyen says:

    Thanks, nice post

Comments are closed.

Generic selectors
Exact matches only
Search in title
Search in content