About Prasanth Gullapalli

Prasanth is a Principal Engineer with more than 7 yrs experience in JEE architecture. He has always been fascinated by the new technologies and emerging trends in software development.

Spring Transactions Visibility

Spring, on initializing application context, creates proxies when it comes across classes marked with @Transactional. @Transactional can be applied at class level or method level. Applying it at class level means all the public methods defined in the class are transactional. The kind of proxy Spring creates i.e. Jdk Proxy or CGLIB proxy, depends on the class in which the method is marked transactional. If the class implements atleast one interface, Spring creates a Jdk dynamic proxy. This proxy implements the same interface as the original class does and intercepts the interface methods with transaction maintenance logic. It delegates the call to the original object composed in it. Suppose the class does not implement any interface, Spring creates a CGLIB proxy. This proxy extends the original class and overrides the public methods. We will take a closer look at this soon. Suppose we have a class defined like this:

public interface BookDao{
	void buyBook(String isbn) throws BookNotFoundException;
	Book findByIsbn(String isbn);
	int deductStock(Book book);
}

public class JdbcBookDao implements BookDao{
	void buyBook(String isbn) throws BookNotFoundException{
		Book book = findByIsbn(isbn);
		if(book == null){
		throw new BookNotFoundException();
		}
		deductStock(book);
	}

	@Transactional(propagation=Propagation.REQUIRES_NEW)
	Book findByIsbn(String isbn){
		Book book = getJdbcTemplate().queryForObject("SELECT * FROM BOOK WHERE ISBN=?",
		ParameterizedBeanPropertyRowMapper.newInstance(Book.class), isbn);
		return book;
	}

	@Transactional(propagation=Propagation.REQUIRES_NEW)
		int deductStock(Book book){
		String sql = "UPDATE BOOK_STOCK SET STOCK=STOCK-1 WHERE BOOK_ID=?";
		return getJdbcTemplate().update(sql, stockIncrement, book.getId());
	}
}

Now will Spring automatically create a transaction on calling bookDao’s findByIsbn from main method? No. We have to declare this in the xml configuration:

<tx:annotation-driven>

So if it does not create a transaction, will it throw an error? Answer is again a No. Spring executes this statement non-transactionally.

Once we have declared ‹tx:annotation-driven›, as rightly expected, Spring creates a Jdk dynamic proxy for JdbcBookDao as the class implements an interface. Now say, we call JdbcBookDao’s buyBook method, how many transactions are created by Spring?

  1. Two: 1 for findByIsbn and 1 more for deductStock.
  2. One: Both findByIsbn and deductStock in same tx.
  3. No transaction at all!

The answer is (3). The default transaction mode is ‘proxy’ for transactions. What this means is method calls made through proxy only will be considered for automatic transaction management by Spring. Now if you carefully observe, buyBook method is not marked transactional. And hence when transactional proxy is created, this method does not get intercepted with transaction management logic as it is not marked @Transactional. In short, buyBook is not overridden in proxy. And hence the method directly gets invoked on the original object. And so the other two methods also gets called on the original object. Remember that only PROXY contains transaction management code. So, as the other methods too get called on original object, Spring does not create a transaction at all. Now will the problem be solved if we mark buyBook as @Transactional? Will Spring creates two separate transactions for each findByIsbn and deductStock methods?

No. Spring creates only one transaction when buyBook() gets called. It would not create any new transaction further as the respective methods gets called on the original object itself and not the proxy. So how to solve this problem?

Can we ask Spring to create a CGLIB proxy()? Now as the proxy is a subclass with overridden public transactional methods, it creates a new transaction for each of the method call? Again NO. The CGLIB proxy does not directly call the method on super class. Here is a rough idea of how it is implemented.

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class MyInterceptor implements MethodInterceptor {
	private Object realObject;

	public MyInterceptor(Object original) {
		this.realObject = original;
	}

	// This method will be called every time the object proxy calls any of its methods
	public Object intercept(Object o, Method method, Object[] args,
                          MethodProxy methodProxy) throws Throwable {
		/**
		* Transaction Management Code
		*/

		// Invoke the method on the real object with the given params
		Object res = method.invoke(realObject, args);

		/**
		* Transaction Management Code
		*/
		return res;
	}
}

import net.sf.cglib.proxy.Enhancer;

public class ProxyCreator(){

	public static T createProxy(T original){
		// Enhancer is CGLIB class which builds a dynamic proxy with new capabilities
		Enhancer e = new Enhancer();
		e.setSuperclass(original.getClass());

		// We have to declare the interceptor whose 'intercept' will be called when methods are called on proxy.
		e.setCallback(new MyInterceptor(original));
		T proxy = (T) e.create();
		return proxy;
	}
}

So as you have seen here, the proxy extends the original class and is composed with it’s object. So when we call buyBook, the proxy creates a transaction and delegates the call to original object. No as findByIsbn and deductStock gets called from buyBook of original object, no new transactions get created.

A quick turnaround solution for this would be as JdbcBookDao is a singleton, get this object from the application context. Now instead of calling methods directly on the object, call it using the reference(to make sure the proxy gets called) Here is how the method may now look like.

public class JdbcBookDao implements BookDao, ApplicationContextAware{
	private ApplicationContext context;
	private BookDao bookDao;

	public void setApplicationContext(ApplicationContext context){
		this.context = context;
	}

	public BookDao getBookDao(){
		bookDao = (BookDao)context.getBean("jdbcBookDao");
	}

	void buyBook(String isbn) throws BookNotFoundException{
		Book book = getBookDao().findByIsbn(isbn);
		if(book == null){
			throw new BookNotFoundException();
		}
		getBookDao().deductStock(book);
	}

	.....
}

Just implemented a crude version to get this up and working. We can definitely improve on the way it is designed. Instead of directly injecting application context into the DAO, may be we can have a kind of helper class which does that. Or another alternative for getting this done is to use programmatic transactions.

A final point to note is that Spring manages transactions only when public methods are marked transactional. For private, protected and package-private methods, Spring does not provide transaction management support. In case of Dynamic proxies, as they implement an interface, all transactional methods are public. So no need to worry about non-public methods. And in case of CGLIB proxies, only public methods get overridden when subclass is created. So even here non-public methods are not considered.

Let me end this discussion with a question. When I tried to proxy target class using ‹tx:annotation-driven proxy-target-class=”true”/›, it really did not work i.e. CGLIB proxies are not created. For this to work, I have to do a minor hack. As spring documentation says clearly if proxy-target-class is enabled on any of ‹tx:annotation-driven›, ‹aop:config› or ‹aop:aspectj-autoproxy›, Spring will enable CGLIB proxy creation on the container. So I just created an empty ‹aop:config proxy-target-class=”true”/›. And not to worry, it started working! Not sure if it’s a bug in Spring itself. Highly appreciated if some one can answer this.
 

Reference: Spring Transactions Visibility from our JCG partner Prasanth Gullapalli at the prasanthnath blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


2 × = six



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
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.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close