Programmatic Transaction Management Example in Spring

You may also like to read:

This tutorial shows an example on programmatic transaction management in Spring. If you want to about Spring Transaction or Programmatic Transaction management in Spring then you can read here. Here we will create annotation based Spring stand alone application to create Programmatic Transaction Management example in Spring. Here we will use H2 in-memory database to create the example. This example is completely Java based configuration and does not use any XMl configuration.

Recommended reading:

Integrate H2 database with Spring Boot

Integrate H2 database with Spring

Spring MVC and JDBC CRUD Example with Zero XML

Declarative Transaction Management in Spring

Prerequisites

JDK 1.8, Spring 5.1.7, H2 1.4.196, Gradle 4.10.2

Creating Annotation based Spring Application

Example with Source Code

Here we will create stand-alone application in Spring framework.

Creating Project

Create gradle based Spring application. The project structure looks to similar figure:

programmatic transaction management in spring

Updating Build Script – build.script

Make sure to have the following dependencies in your build.gradle script.

buildscript {
	ext {
		springVersion = '5.1.7.RELEASE'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("io.spring.gradle:dependency-management-plugin:1.0.7.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'io.spring.dependency-management'

sourceCompatibility = 1.8
targetCompatibility = 1.8

mainClassName = 'com.jeejava.spring.tx.Application'
jar {
    baseName = 'spring-declarative-transaction-management'
    version =  '0.0.1'
}

repositories {
	mavenLocal()
    mavenCentral()
}

dependencies {
	compile("org.springframework:spring-core:${springVersion}")
	compile("org.springframework:spring-beans:${springVersion}")
	compile("org.springframework:spring-context:${springVersion}")
	compile("org.springframework:spring-jdbc:${springVersion}")
	compile("org.springframework:spring-tx:${springVersion}")
	runtime("com.h2database:h2:1.4.196")
}

You may or may not declare the main class in the above build script.

Creating SQL Scripts

As we are using H2 in-memory database to create our example. So we will create table and insert data using SQL scripts.

Creating Table Script

Create below table SQL script – create-table.sql and put it under src/main/resources/sql directory.

CREATE TABLE `item` (
  `item_id` int(10) NOT NULL AUTO_INCREMENT,
  `item_name` varchar(45) NOT NULL,
  `item_desc` text,
  `item_price` double NOT NULL DEFAULT '0',
  PRIMARY KEY (`item_id`)
);

Creating Insert Script

Create below insert-data.sql script and put it under src/main/resources/sql directory.

insert into `item`(`item_id`,`item_name`,`item_desc`,`item_price`)
values (1,'CD','CD is a compact disk',100),
(2,'DVD','DVD is larger than CD in size',150),
(3,'ABC','ABC test description',24),
(4,'XYZ','XYZ test description',25.32),
(5,'CD Player','CD player is used to play CD',30.02),
(6,'New Item1','New Item1 Desc',125);

Creating Spring Config Class

Create Spring configuration class to create datasource, jdbc template, configuration for base package etc.

package com.jeejava.spring.tx.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

@Configuration
@ComponentScan(basePackages = "com.jeejava.spring.tx")
public class Config {

	@Bean
	public DataSource dataSource() {
		EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
		EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2) // .H2 or .DERBY, etc.
				.addScript("sql/create-table.sql").addScript("sql/insert-data.sql").build();
		return db;
	}

	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource) {

		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

		return jdbcTemplate;
	}

}

In the above class we have used annotation @Configuration to indicate it is a configuration class.

We have configured our base package to let Spring container know where our annotated classes reside.

We have created two beans – Datasource and JdbcTemplate, which are required to perform our database operations.

Creating Model Class

We will create a model class to represent each row of the table data to corresponding Java class attributes.

package com.jeejava.spring.tx.model;

public class Item {

	private Long itemId;
	private String itemName;
	private String itemDesc;
	private Double itemPrice;

	public Long getItemId() {
		return itemId;
	}

	public void setItemId(Long itemId) {
		this.itemId = itemId;
	}

	public String getItemName() {
		return itemName;
	}

	public void setItemName(String itemName) {
		this.itemName = itemName;
	}

	public String getItemDesc() {
		return itemDesc;
	}

	public void setItemDesc(String itemDesc) {
		this.itemDesc = itemDesc;
	}

	public Double getItemPrice() {
		return itemPrice;
	}

	public void setItemPrice(Double itemPrice) {
		this.itemPrice = itemPrice;
	}

	@Override
	public String toString() {
		return "Item [itemId=" + itemId + ", itemName=" + itemName + ", itemDesc=" + itemDesc + ", itemPrice="
				+ itemPrice + "]";
	}

}

Creating Row Mapper Class

We have created model class in the above but we need to map each row column with Java class attribute and for that we need Row Mapper class.

package com.jeejava.spring.tx.row.mapper;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.jeejava.spring.tx.model.Item;

public class ItemRowMapper implements RowMapper<Item> {

	@Override
	public Item mapRow(ResultSet rs, int rowNum) throws SQLException {
		Item item = new Item();
		item.setItemId(rs.getLong("item_id"));
		item.setItemName(rs.getString("item_name"));
		item.setItemDesc(rs.getString("item_desc"));
		item.setItemPrice(rs.getDouble("item_price"));
		return item;
	}

}

Notice in the above class we are mapping the correct data type for each column value with Java class attribute.

Applying Programmatic Transactions

Now we have two types of programmatic transactions – Platform Transaction Manager and Transaction Template.

Using Platform Transaction Manager

The DataSourceTransactionManager bean, which we need to have declared in the Config.java file, is of type of PlatformTransactionManager. Hence we can directly use to handle programmatic transactions.

Modifying Config.java

Therefore modify our existing Config.java class to have the following bean declaration:

...
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
	return new DataSourceTransactionManager(dataSource);
}
...

Creating DAO Class

We will interact with database, so we need to create DAO class or Repository class for performing database operations.

Create a class with @Repository annotation to auto-pickup by Spring container. You may also use @Component annotation if you think @Repository is not relevant on this DAO class.

package com.jeejava.spring.tx.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.jeejava.spring.tx.model.Item;
import com.jeejava.spring.tx.row.mapper.ItemRowMapper;

@Repository
public class ItemDaoPfTx {

	@Autowired
	JdbcTemplate jdbcTemplate;

	@Autowired
	PlatformTransactionManager transactionManager;

	public List<Item> getItems() {
		String sql = "SELECT * FROM items";
		List<Item> items = new ArrayList<>();
		items = jdbcTemplate.query(sql, new ItemRowMapper());
		return items;
	}

	public void addItem(Item item) {
		TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
		TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
		try {
			String sql = "INSERT INTO items(item_id, item_name, item_desc, item_price) VALUES (?, ?, ?, ?);";
			jdbcTemplate.update(sql,
					new Object[] { item.getItemId(), item.getItemName(), item.getItemDesc(), item.getItemPrice() });
			transactionManager.commit(transactionStatus);
		} catch (Exception ex) {
			transactionManager.rollback(transactionStatus);
		}
	}

	public void updateItem(Item item) {
		TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
		TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
		try {
			String sql = "UPDATE items SET item_name=?, item_desc=?, item_price=? WHERE item_id=?;";
			jdbcTemplate.update(sql,
					new Object[] { item.getItemName(), item.getItemDesc(), item.getItemPrice(), item.getItemId() });
			transactionManager.commit(transactionStatus);
		} catch (Exception ex) {
			transactionManager.rollback(transactionStatus);
		}
	}

	public void deleteItem(Item item) throws Exception {
		TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(
				TransactionDefinition.PROPAGATION_REQUIRED);
		TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
		try {
			String sql = "DELETE FROM items WHERE item_id=?;";
			jdbcTemplate.update(sql, new Object[] { item.getItemId() });
			transactionManager.commit(transactionStatus);
		} catch (Exception ex) {
			ex.printStackTrace();
			transactionManager.rollback(transactionStatus);
		}
	}

}

In the above DAO class that uses PlatformTransactionManager, the Transaction Management code can be seen in addItem, updateItem and deleteItem method.

So lets understand:

Step 1 is to create an object of DefaultTransactionDefinition. This object exposes various setter methods to configure the transaction accordingly.

Step 2 is to to create the TransactionStatus object using the transaction definition we created in Step 1. Eventually this TransactionStatus will be used to commit or rollback the transaction like platformTransactionManager.commit(transactionStatus); or platformTransactionManager.rollback(transactionStatus);

Creating Service Class

Create below Spring service class to perform business logic. We annotated the class using @Service to indicate it as a service class.

package com.jeejava.spring.tx.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.jeejava.spring.tx.dao.ItemDaoPfTx;
import com.jeejava.spring.tx.model.Item;

@Service
public class ItemService {

	@Autowired
	private ItemDaoPfTx itemDao;

	public List<Item> getItems() {
		return itemDao.getItems();
	}

	public void addItem(Item item) {
		itemDao.addItem(item);
	}

	public void updateItem(Item item) {
		itemDao.updateItem(item);
	}

	public void deleteItem(Item item) throws Exception {
		itemDao.deleteItem(item);
	}

}

Here in the above service class we did not apply any transaction because we are managing transaction programmatically.

Creating Main Class

As we know this is a stand alone Spring application, so we need to have main class or Junit class to test our application. Here I am creating main class to test our application.

package com.jeejava.spring.tx;

import java.util.List;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.jeejava.spring.tx.config.Config;
import com.jeejava.spring.tx.model.Item;
import com.jeejava.spring.tx.service.ItemService;

public class Application {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

		context.register(Config.class);
		context.refresh();

		ItemService service = context.getBean(ItemService.class);

		System.out.println("----Available Items----");
		List<Item> items = service.getItems();
		items.forEach(i -> System.out.println(i));

		System.out.println();
		try {
			Item delItem = new Item();
			delItem.setItemId(6l);
			service.deleteItem(delItem);
			System.out.println("Item Successfully Deleted.");
		} catch (Exception ex) {
			System.out.println("Item was not deleted.");
			System.out.println("Transaction rolled back due to Exception.");
		}
		System.out.println();

		context.close();
	}

}

Notice in the above class, how do we get the bean from annotation based Java classes. We have loaded the Config.class because this class configures everything required for the application.

Testing the Application

Run the above class you will see below output on the Eclipse console.

----Available Items----
Item [itemId=1, itemName=CD, itemDesc=CD is a compact disk, itemPrice=100.0]
Item [itemId=2, itemName=DVD, itemDesc=DVD is larger than CD in size, itemPrice=150.0]
Item [itemId=3, itemName=ABC, itemDesc=ABC test description, itemPrice=24.0]
Item [itemId=4, itemName=XYZ, itemDesc=XYZ test description, itemPrice=25.32]
Item [itemId=5, itemName=CD Player, itemDesc=CD player is used to play CD, itemPrice=30.02]
Item [itemId=6, itemName=New Item1, itemDesc=New Item1 Desc, itemPrice=125.0]

Item Successfully Deleted.

So in the above output, it is clear that there is no exception occurred and new item was added successfully. One item was successfully deleted also.

Using Transaction Template

Spring TransactionTemplate is similar to JdbcTemplate and is used to abstract away the boilerplate code from the user. It provides simple callback methods which are automatically wrapped in a transaction. It also exposes direct setter methods for configuring various properties of transactions like setRollbackOnly etc. The Spring’s TransactionTemplate is initialized by providing a reference of DataSourceTransactionManager bean.

Modifying Config.java

We need to add TransactionTemplate bean to our existing config class. Do not remove anything from the existing Config.java class.

...
@Bean
public TransactionTemplate transactionTemplate(DataSourceTransactionManager transactionManager) {
	return new TransactionTemplate(transactionManager);
}
...

We defined bean DataSourceTransactionManager for our Platform Transaction Manager earlier and it is required for our Transaction Template as you see in the above, it refers to.

Creating DAO Class

Create below DAO class to have our TransactionTemplate.

Using TransactionTemplate, we dont need to create a TransactionDefinition and Transaction object. The TransactionTemplate provides a callback method called execute where in we write our business logic that we want to wrap in a transaction. There are two types of callback methods that we can use to wrap our code i.e TransactionCallbackWithoutResult and TransactionCallback(T).

package com.jeejava.spring.tx.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.jeejava.spring.tx.model.Item;
import com.jeejava.spring.tx.row.mapper.ItemRowMapper;

@Repository
public class ItemDaoTxTemp {

	@Autowired
	JdbcTemplate jdbcTemplate;
	@Autowired
	TransactionTemplate transactionTemplate;

	public List<Item> getItems() {
		String sql = "SELECT * FROM item";
		List<Item> items = new ArrayList<>();
		items = jdbcTemplate.query(sql, new ItemRowMapper());
		return items;
	}

	public void addItem(final Item item) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				try {
					String sql = "INSERT INTO item(item_id, item_name, item_desc, item_price) VALUES (?, ?, ?, ?);";
					jdbcTemplate.update(sql, new Object[] { item.getItemId(), item.getItemName(), item.getItemDesc(),
							item.getItemPrice() });
				} catch (Exception e) {
					transactionStatus.setRollbackOnly();
				}
			}
		});
	}

	public void updateItem(final Item item) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				try {
					String sql = "UPDATE item SET item_name=?, item_desc=?, item_price=? WHERE item_id=?;";
					jdbcTemplate.update(sql, new Object[] { item.getItemName(), item.getItemDesc(), item.getItemPrice(),
							item.getItemId() });
				} catch (Exception e) {
					transactionStatus.setRollbackOnly();
				}
			}
		});
	}

	public void deleteItem(final Item item) throws Exception {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				try {
					String sql = "DELETE FROM item WHERE item_id=?;";
					jdbcTemplate.update(sql, new Object[] { item.getItemId() });
				} catch (Exception e) {
					transactionStatus.setRollbackOnly();
				}
			}
		});
	}

}

Modifying Service Class

Modify the existing service that we created during Platform Transaction Manager as shown below and rest of the code remains same.

@Autowired
// private ItemDaoPfTx itemDao;
private ItemDaoTxTemp itemDao;

Running the Application

Now run the main class that we created during our Platform Transaction Manager, you will see the below output, which is same as earlier.

----Available Items----
Item [itemId=1, itemName=CD, itemDesc=CD is a compact disk, itemPrice=100.0]
Item [itemId=2, itemName=DVD, itemDesc=DVD is larger than CD in size, itemPrice=150.0]
Item [itemId=3, itemName=ABC, itemDesc=ABC test description, itemPrice=24.0]
Item [itemId=4, itemName=XYZ, itemDesc=XYZ test description, itemPrice=25.32]
Item [itemId=5, itemName=CD Player, itemDesc=CD player is used to play CD, itemPrice=30.02]
Item [itemId=6, itemName=New Item1, itemDesc=New Item1 Desc, itemPrice=125.0]

Item Successfully Deleted.

That’s all. Hope you got an idea on Pragrammatic Transaction Example in Spring.

Source Code

You can download source code.

Thanks for reading.

Programmatic Transaction Management Example in Spring
I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger and writing blogs is my passion. Connect me on Roy Tutorials | Twitter | Facebook | Linkedin | Reddit | Ello | Tumblr

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top