About 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

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:

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


nine + = 16



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books