Core Java

Java Nested Transaction using ThreadLocal in POJO

Mostly nested transaction was implemented using EJB , now we try to implement the nested transaction on POJO. Here we have used the feature of ThreadLocal.

Understanding Nested Transaction

Transactions can be nested one inside another.  So the inner transaction or outer transaction can be rollback or commit without affecting the other transaction.

When a new transaction is created then it comes under the outer transaction. Once the inner transaction is completed in either case commit or rollback, the outer transaction can perform either commit or rollback without related to inner transaction. First close the innermost transaction and move on to the outer.

Image1

Implementing using Simple POJO

Creating interface as below:

importjava.sql.Connection;

publicinterfaceTransactionManager {

	Connection getConnection();
	voidbeginTransaction();
	void commit();
	void rollback();
}

Creating Transaction Manager class as below:

importjava.sql.Connection;
importjava.sql.DriverManager;
importjava.sql.SQLException;
importjava.util.Stack;

publicclassTransactionManagerStackImplimplementsTransactionManager {
	
	private Stack<Connection>connections = new Stack<Connection>();

	@Override
	public Connection getConnection() {

		if (connections.isEmpty()) {
			this.addConn();
		}

		returnconnections.peek();
	}

	@Override
	publicvoidbeginTransaction() {
		this.addConn();
	}

	@Override
	publicvoid commit() {
		try {
			if (connections.peek() != null&& !connections.peek().isClosed()) {
				System.out.println(connections.peek().toString() +"--Commit---");
				connections.peek().commit();
				connections.pop().close();
			}

		} catch (SQLException e) {
			e.printStackTrace();
		}

	}

	@Override
	publicvoid rollback() {
		try {

			if (connections.peek() != null&& !connections.peek().isClosed()) {
				System.out.println(connections.peek().toString() +"--Rollback---");
				connections.peek().rollback();
				connections.pop().close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}

	privatevoidaddConn() {
		try {
			Connection con = this.getMysqlConnection();
			con.setAutoCommit(false);
			connections.push(con);
			System.out.println(con.toString() +"--Conection---");
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}

	private Connection getMysqlConnection() {
		returngetConnection("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/testdb", "test", "test12345");
	}

	private Connection getConnection(String driver, String connection,
			String user, String password) {

		try {
			Class.forName(driver);
			returnDriverManager.getConnection(connection, user, password);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}

		returnnull;

	}
}

Here we have created a Stack:

private Stack<Connection> connections = new Stack<Connection>();

As the transactions are created as LIFO (Stack) we have used Stack from Java API to maintain connections for each transaction:

public void beginTransaction()

Begin transaction to begin a new transaction and add the connection to the Stack. AutoCommit has been set to false:

public Connection getConnection()

Get Connection for the current transactions. If not exist it will create and add to stack:

public void commit()

Commit the current transaction and close the connection, also removed from stack:

public void rollback()

Rollback the current transaction and close the connection, also removed from stack.

The above class TransactionManagerStackImpl   will create nested transaction for single thread.

Nested Transaction for Multithreads

In case of multithreaded application, each thread has separate transaction and nested transaction.

We came up using ThreadLocal to manage the stack of connections.

importjava.sql.Connection;

publicclassTransactionManagerThreadLocalimplementsTransactionManager {
	
	privatestaticfinalThreadLocal<TransactionManager>tranManager = newThreadLocal<TransactionManager>() {
		
	protectedTransactionManagerinitialValue() {
		System.out.println(this.toString() + "--Thread Local Initialize--");
	returnnewTransactionManagerStackImpl();
	    }
	  };

	@Override
	publicvoidbeginTransaction() {
		tranManager.get().beginTransaction();
	}

	@Override
	publicvoid commit() {
		tranManager.get().commit();
	}

	@Override
	publicvoid rollback() {
		tranManager.get().rollback();
	}

	@Override
	public Connection getConnection() {
		returntranManager.get().getConnection();
	}
}

Here we initialize TransactionManagerStackImpl to create nested transaction inside the thread.

Testing

For testing this above, commit inner transaction and rollback outer transaction.

importjava.sql.Connection;

publicclassNestedMainimplements Runnable {
	
	privateintv = 0;
	private String name;
	
	NestedMain(int v, String name) {
		this.v = v;
		this.name = name;
	}

	publicstaticvoid main(String[] args) throws Exception{
		
		for (inti = 0; i< 3; i++) {
			NestedMain main = newNestedMain(i * 10, "Ravi" + i);
			new Thread(main).start();
		}
	}

	@Override
	publicvoid run() {
		
		try {
			TransactionManagerThreadLocal local = newTransactionManagerThreadLocal();
			
			// Transaction 1 ( outer )
			local.beginTransaction();
			Connection con = local.getConnection();
			String sql = "INSERT INTO test_tran (emp_id, name) VALUES ('1"+v+"', '"+ name+v+"')";
			this.insert(con, sql);
	
				// Transaction 2 ( Inner )
				local.beginTransaction();
				con = local.getConnection();
				sql = "INSERT INTO test_tran (emp_id, name) VALUES ('2"+v+"', '"+ name+v+"')";
				this.insert(con, sql);
				local.commit(); // Committing 2

			local.rollback(); // Rollback 1 Outer

		} catch (Exception e) {
			e.printStackTrace();
		}

Result

com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.mysql.jdbc.JDBC4Connection@10dd1f7--Conection---
com.mysql.jdbc.JDBC4Connection@1813fac--Conection---
com.mysql.jdbc.JDBC4Connection@136228--Conection---
com.mysql.jdbc.JDBC4Connection@1855af5--Conection---
com.mysql.jdbc.JDBC4Connection@e39a3e--Conection---
com.mysql.jdbc.JDBC4Connection@1855af5--Commit---
com.mysql.jdbc.JDBC4Connection@e39a3e--Commit---
com.mysql.jdbc.JDBC4Connection@9fbe93--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Commit---
com.mysql.jdbc.JDBC4Connection@10dd1f7--Rollback---
com.mysql.jdbc.JDBC4Connection@1813fac--Rollback---
com.mysql.jdbc.JDBC4Connection@136228--Rollback---
nameemp_id
Ravi220220
Ravi0020
Ravi110210

When rollback inner transaction and commit outer transaction:

com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.mysql.jdbc.JDBC4Connection@9f2a0b--Conection---
com.mysql.jdbc.JDBC4Connection@136228--Conection---
com.mysql.jdbc.JDBC4Connection@1c672d0--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Conection---
com.mysql.jdbc.JDBC4Connection@1858610--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Rollback---
com.mysql.jdbc.JDBC4Connection@1858610--Rollback---
com.mysql.jdbc.JDBC4Connection@1a5ab41--Conection---
com.mysql.jdbc.JDBC4Connection@1a5ab41--Rollback---
com.mysql.jdbc.JDBC4Connection@9f2a0b--Commit---
com.mysql.jdbc.JDBC4Connection@136228--Commit---
com.mysql.jdbc.JDBC4Connection@1c672d0--Commit---
nameemp_id
Ravi0010
Ravi220120
Ravi110110

Resource:

Baskaran Chinnusamy

Baskaran Chinnusamy is a software engineer has developed frameworks for companies. He also involved in different application developments and performance tunings. Currently he leads project development and designing applications
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Theodore
Theodore
7 years ago

Good to get a look at a basic nested transaction implementation. This gives a little peek into the JavaEE and Spring transactions.

Back to top button