Spring Transaction with @Transactional explained

The most easy way to Spring Transaction is to use @Transactional. The following is basic step to use it.

Required spring component

spring-context.jar, spring-tx.jar, spring-jdbc.jar and additional dependencies

Spring context

  • <tx:annotation-driven> must be declared to enable @Transactional
  • TransactionManager must be declared. If the id is not “transactionManager“, it must be set with <tx:annotation-driven>’s transaction-manager attribute
  • Sample context.xml

sample_context.xml

  • TransactionManager’s id is “testTransactionManager”
  • “testTransactionManager” is mapped with “testDataSource”
  • Spring transaction is enabled with <tx:annotation-driven>

Bean with @Transactional

  • @Transactional can be declared at interface, class or method level
  • If @Transactional is declared at a method, it must be public
  • Sample dao
package test.dao;

import javax.sql.DataSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

@Repository
public class TestDao {
	private JdbcTemplate jdbcTemplate;

	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	@Transactional
	public void insertData(String col1, String col2) {
		this.jdbcTemplate.update("INSERT INTO TRANSACTION_TEST(COL1, COL2) VALUES(?, ?)", col1, col2);
	}
}

Accessing transactional bean

  • Spring Transaction uses AOP to implement transaction
  • Bean with @Transactional is wrapped with a AOP Proxy
  • Proxy object is generated with JDK dynamic proxy or CGLIB-based proxy. If the bean is interface-based, JDK proxy is used. But if the bean is without interface or CGLIB is foreced, CGLIB is used. (CGLIB is forced if <tx:annotation-driven proxy-target-class=”true” /> is set)
  • Accessing transactional bean must be through Spring context. If accessed directly, the reference is not proxy but dao itself. Therefore transaction is not applied
  • Sample code
package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.dao.TestDao;
import test.dao.TestMultiDao;

public class TransactionTest {

	public static void main(String[] args) {
		TransactionTest transactionTest = new TransactionTest();
		ApplicationContext context = transactionTest.getSpringContext();
		TestDao testDao = context.getBean(TestDao.class);
		testDao.insertData("1", "111");
		testDao.insertData("2", "222");
	}

    private ApplicationContext getSpringContext(){
        String contextFileName = "context.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {contextFileName});
        return context;
    }
}

Internal working of Spring Transaction

Within transactional method, underlying jdbc connection is set as manual commit and transaction is committed before the method ends. To prove it, check the following log. (When running above code)

DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager – Creating new transaction with name [test.dao.TestDao.insertData]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ”
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager – Acquired Connection […] for JDBC transaction
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager – Switching JDBC Connection […] to manual commit
DEBUG: org.springframework.jdbc.core.JdbcTemplate – Executing prepared SQL update
DEBUG: org.springframework.jdbc.core.JdbcTemplate – Executing prepared SQL statement [INSERT INTO TRANSACTION_TEST(COL1, COL2) VALUES(?, ?)]
DEBUG: org.springframework.jdbc.core.JdbcTemplate – SQL update affected 1 rows
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager – Initiating transaction commit
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager – Committing JDBC transaction on Connection […]
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager – Releasing JDBC Connection […] after transaction
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils – Returning JDBC Connection to DataSource

But if @Transactional is not used, no message about transaction is shown

Worst practice

If transactional bean or method is accessed directly, no transaction is applied. The following is the example

@Repository
public class TestDao {

	@Transactional
	public void insertData(String col1, String col2) {
		this.jdbcTemplate.update("INSERT INTO TRANSACTION_TEST(COL1, COL2) VALUES(?, ?)", col1, col2);
	}

	public void insertDataWithoutTransaction(String col1, String col2) {
		insertData(col1, col2);
	}
}

Calling insertDataWithoutTransaction() is without transaction although it calls transactional insertData() inside, because insertData() is accessed through this (TestDao object), not through proxy

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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