MrBool
You must be logged in to give feedback. Click here to login
[Close]

You must be logged to download.

Click here to login

[Close]

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

[Close]

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Spring transaction handling: Transaction-per-request with AOP

This article will discuss about ways you may handle your Spring transactions better by making use of AOP (Aspect-Oriented Programming).

[close]

You didn't like the quality of this content?

Would you like to comment what you didn't like?

Introduction

In the previous article, I introduced Spring’s support for aspect-oriented programming, and discussed a particular application for it: automatically wrapping each request to any servlet in an application with a transaction. In this article, I will show how to implement that with the building blocks introduced in the last part, and describe the similar (albeit more advanced) support integrated into Spring for managing transactions.

An application that needs transactions

In order to demonstrate this technique, we need an application that can take advantage of it. So, I’ll add to the application that was built in the previous article a new servlet, and this one will need a database. I’m using MySQL because I have a copy on my development machine already, but any modern relational database management system should do the job. You do obviously need a database that supports transactional updates, so I’m going to configure my MySQL table to use the InnoDB engine (MySQL’s default MyISAM engine does not support transactions).

Listing 1: The SQL statements to configure the database

 
create database test;
use test
grant all on test.* to 'test'@'localhost' identified by 'test';
create table accounts (
   id int primary key not null,
   balance decimal(9,2) not null
) engine=innodb;
insert into accounts values (1, 0);
insert into accounts values (2, 0);
insert into accounts values (3, 0);

Now we need to configure a data source in our Spring context. I’ll also configure a simple connection manager bean, because we need to ensure that all code within each transaction always uses the same connection to the database. My code will use the connection manager to get a database connection object, and the connection manager will get the object from the data source.

Listing 2: new bean definitions in applicationContext.xml

 
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
	    <property name="driver"><bean class="com.mysql.jdbc.Driver"/></property>
	    <property name="url" value="jdbc:mysql://localhost/test"/>
	    <property name="username" value="test"/>
	    <property name="password" value="test"/>
</bean>
	<bean id="connectionManager" class="com.example.springtransactions.ConnectionManager" />

Listing 3: The ConnectionManager class

 
public class ConnectionManager
{
private DataSource dataSource;
	private ThreadLocal<Connection> connections = new ThreadLocal<Connection> () {
		@Override
		protected Connection initialValue ()
		{
		    return getNewConnection ();
		}
	};
	
	public void setDataSource (DataSource dataSource)
    	{
	    this.dataSource = dataSource;
    	}
	
	@SuppressWarnings ("resource")
    	public Connection getConnection ()
	{
		Connection result = connections.get ();
		try
        	{
	        if (result.isClosed ())
	        	connections.set (result = getNewConnection ());
        	}
        	catch (SQLException e)
        	{
        		connections.set (result = getNewConnection ());
        	}
		return result;
	}

	public Connection getNewConnection ()
    	{
	    try
	    {
	        return dataSource.getConnection ();
	    }
	    catch (SQLException e)
	    {
	    	throw new RuntimeException ("Couldn't get SQL connection", e);
	    }
    	}
}

The connection manager simply uses the data source (which Spring autowires for us as it is stored in a property with the same name as the bean, and I’ve set default-autowire=“byName” in applicationContext.xml) to get one connection per thread. It also checks before returning the connection for some obvious error conditions and gets new connections in those cases. If it fails, it throws a runtime exception so we don’t have to catch it in our code (we could use an @AfterThrow advice to catch such exceptions and handle them neatly if we wanted, but for now the debugging information shown in the default error pages is helpful).

So now we have a database, we need some code that uses it. We’ll add a new servlet as follows:

Listing 4: Add new servlet

public class TransactionalServlet extends HttpServlet
{
	private static final long serialVersionUID = 1L;

	ConnectionManager connectionManager;
	public void setConnectionManager (ConnectionManager connectionManager)
	{
		this.connectionManager = connectionManager;
	}

	protected void doGet (HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException
	{
		PrintWriter writer = response.getWriter ();
		writer.println (
		    "<html><body>" +
		    "<form method='post'>" +
		    "Account 1 id: <input type='text' name='id1'> Credit: <input type='text' name='cr1'><br>" +
		    "Account 2 id: <input type='text' name='id2'> Credit: <input type='text' name='cr2'><br>" +
		    "<input type=submit></form>" +
		    "<table>");
		Connection c = connectionManager.getConnection ();
		try (Statement s = c.createStatement ())
		{
			ResultSet r = s.executeQuery ("select * from accounts");
			while (r.next ())
				writer.println (
				    "<tr><td>" + r.getInt ("id") + "</td>" +
				        "<td>" + r.getDouble ("balance") + "</td></tr>");
			r.close ();
		}
		catch (SQLException ignored)
		{
		}
		writer.println ("</table></body></html>");
	}

	protected void doPost (HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException
	{
		Connection c = connectionManager.getConnection ();
		try (PreparedStatement s = c.prepareStatement (
"update accounts set balance = balance + ? where id = ?"))
		{
			s.setDouble (1, Double.parseDouble (request.getParameter ("cr1")));
			s.setInt (2, Integer.parseInt (request.getParameter ("id1")));
			s.executeUpdate ();

			s.setDouble (1, Double.parseDouble (request.getParameter ("cr2")));
			s.setInt (2, Integer.parseInt (request.getParameter ("id2")));
			s.executeUpdate ();

			// redirect back to self to implement the post then redirect pattern
			response.sendRedirect (request.getRequestURL ().toString ());
		}
		catch (SQLException e)
		{
			throw new ServletException ("SQL error", e);
		}

	}

}

The servlet is quite straightforward - when it receives a get request it displays a simple form and the current state of the database. On a post request it attempts to change the balance of two accounts and then redirects back to its own URL to display the form and updated database state (in case you haven’t come across the idea before, this post-then-redirect pattern helps avoid the problem that navigating around your browser history you sometimes get pages you can’t see because you’d need to resubmit information that will change the database).

Let’s try using it. Configure a reference to it in your servlets map in applicationContext.xml and fire up your browser. Use it to add some money to a few accounts, and you should see their balances changing.

Now let’s try some more realistic actions. Transfer 10 units from account 1 to account 2 by putting in ‘1’ ‘-10’ ‘2’ ‘10’ and submitting. Now reverse the action: ‘1’ ‘10’ ‘2q’ ‘-10’, submit - oops, I hit ‘q’ by mistake just before tab and an error screen came up. Press back and reload the form, and you’ll notice that account 1 was updated but account 2 wasn’t. We need to roll any changes back if an error occurs half way through the update.

Adding a TransactionManager

We know how to fix this now. Let’s add a new object to our applicationContext.xml:

Listing 5: Adding the TransactionManager

 
	<bean id="transactionManager" class="com.example.springtransactions.TransactionManager" />

And the code itself is relatively simple:

Listing 6: The TransactionManager class

@Aspect
public class TransactionManager
{
	ConnectionManager connectionManager;
	public void setConnectionManager (ConnectionManager connectionManager)
{
	    this.connectionManager = connectionManager;
}
	
	@Before("execution(* javax.servlet.http.HttpServlet.service(..))")
	public void startTransaction () throws SQLException
	{
		Connection connection = connectionManager.getConnection ();
		connection.setAutoCommit (false);
		// roll back any uncommitted transaction that may already be on the connection
		connection.rollback ();	
	}
	
	@AfterThrowing("execution(* javax.servlet.http.HttpServlet.service(..))")
	public void rollbackTransaction () throws SQLException
	{
		connectionManager.getConnection ().rollback ();
	}
	
	@AfterReturning("execution(* javax.servlet.http.HttpServlet.service(..))")
	public void commitTransaction () throws SQLException
	{
		connectionManager.getConnection ().commit ();
	}
}

Restart the server, and try the same test again - this time, after the failed transaction the database hasn’t changed. Transactions are working as intended. And, if we now add any more servlets to this application, they all get transaction support without us even having to consider it.

Of course, this is an extremely simple application and the way we have done this is rather inefficient, but this technique can be adapted for all kinds of application from the smallest to the largest.

Spring declarative transactions

And this is just what Spring’s declarative transactions do. Spring has an integrated transaction manager, similar to the one above (but much more complex) that can be configured to behave however your application wants it to. It also has a facility like the ConnectionManager I implemented, but which doesn’t only support JDBC but is also integrated to Hibernate, JPA, JDO and iBatis. It can also be integrated easily with other kinds of transaction management than JDBC transactions.

So should you use that rather than writing your own like I just did? I would argue that for many applications the approach I have taken is simpler, and there is an advantage in understanding exactly how the code you’re working with works. The less complex a system is, the less likely it is to fail because you don’t understand it fully. A transactional wrapper around each web request is a simple system that is unlikely to fail in hard-to-predict ways.

But for many projects, the requirements are more complex. Perhaps this system performs too slowly (the transactions generated by methods that have read-only access are somewhat redundant, for example), or maybe there is a genuine need for multiple transactions in a single request. For these applications, Spring’s transaction management is a better fit.

This is all for this article. Hope you liked it. See you next time.



My main area of specialization is Java and J2EE. I have worked on many international projects like Recorders,Websites,Crawlers etc.Also i am an Oracle Certified java professional as well as DB2 certified

What did you think of this post?
Services
Know how to keep MrBool Online
SUPPORT US
SUPPORT US
With your help, we can keep providing free content and helping you to be a better professional
support us
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually � in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits � in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login