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
- 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