Spring declarative transaction management XML based
All other spring transaction tutorials:
- Spring declarative transaction management Annotation based
- Spring programmatic transaction using TransactionTemplate
- Spring Programmatic Transactions by PlatformTransactionManager
Spring declarative transaction management is famous for first choice of most users compare to programmatic transaction because its configuration is easy with very minimum impact on programming code that is why it’s very consistent and very suitable for lightweight container.
Spring declarative transaction management is possible by using Spring AOP even though as spring transactional aspects programming code comes with Spring distribution and can be used in boilerplate style and concept of AOP don’t need to be understood to write effective code because it’s configuration based.
There are two way of implementing declarative transaction in spring as listed below:
- Using XML based transactional advice and AOP configuration
- By using @Transactional annotation
Here we will discuss about XML based transactional advice and AOP
Spring declarative transaction implementation description:
XML based spring transaction management is concept where its enable via AOP proxies and it’s transaction advice is driven by using metadata xml. The combination of AOP and transaction metadata gives results AOP proxy where it uses TransactionalInterceptor in combination with appropriate PlatformTransactionManager. You will see in example below to understand better. Let’s start its implementation:
Below are needed to run this example:
- Eclipse ( We are using eclipse Kepler. You could also download eclipse from eclipse.org/downloads)
- MySQL data base (Install MySQL Community Server (GPL) version in your system : MySQL Community Server). We are using version 5.6 ( If you are not sure how to install it please use this link : Install MySQL server )
- Maven 3.0.4
Below are steps:
Step 1. Create table to MySQL database: Please use below script to create table and insert sample data for test to the MySQL database:
DROP TABLE person GO CREATE TABLE person ( id int NOT NULL AUTO_INCREMENT, First_Name varchar(25) NULL, Last_Name varchar(25) NULL, Street_Name varchar(25) NULL, City varchar(25) NULL, State varchar(25) NULL, Country varchar(25) NULL, PRIMARY KEY (id) ) GO
Step 2: Create dynamic web project in eclipse name: SpringDeclarativeTransaction (Please use this link if you are not sure how to create maven project in eclipse: Create maven project in eclipse) . Maven project structure:
Step 3: Create three package inside src folder
- com.javahonk
- com.javahonk.dao
- com.javahonk.di.bean
Step 4: Create interface IPersonDAO inside com.javahonk.dao package. This will be our service interface and we will make all method of this interface transactional. Please copy paste below code:
package com.javahonk.dao; import com.javahonk.di.bean.Person; public interface IPersonDAO { int insertUser(Person person); void deletePerson(int personID); void selectAllPerson(); void selectPersonByName(); }
Step 5: Create implementation of above interface and where we will implement all of its method and provide details implementation of transaction. Create class PersonDAO implements IPersonDAO inside com.javahonk.dao package and copy paste below code:
package com.javahonk.dao; import java.sql.Types; import java.util.Iterator; import java.util.List; import java.util.Map; import org.springframework.jdbc.core.JdbcTemplate; import com.javahonk.di.bean.Person; public class PersonDAO implements IPersonDAO { private JdbcTemplate jdbcTemplate; @Override public int insertUser(Person person) { String inserQuery = "INSERT INTO person(First_Name, " + "Last_Name, Street_Name, City, State, Country) " + "VALUES(?, ?, ?, ?, ?, ?)"; Object[] params = new Object[] { person.getFirstName(), person.getLastName(), person.getStreet(),person.getCity(),person.getState(), person.getCountry()}; int[] types = new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR }; int value = jdbcTemplate.update(inserQuery, params, types); System.out.println("\nPerson inserted to the table"); return value; } @Override public void deletePerson(int personID) { String deletePerson = "DELETE FROM person WHERE id =?"; Object[] params = new Object[] { personID}; int[] types = new int[] { Types.VARCHAR}; jdbcTemplate.update(deletePerson, params, types); System.out.println("\nPerson with id 1 deleted from " + "the table\n"); } @Override public void selectAllPerson() { System.out.println("\nList of person in the table\n"); String selectAllPerson = "SELECT * FROM person"; List<Map<String, Object>> listOfPerson = jdbcTemplate .queryForList(selectAllPerson); for (Iterator<Map<String, Object>> iterator = listOfPerson.iterator(); iterator.hasNext();) { Map<String, Object> map = (Map<String, Object>) iterator.next(); System.out.println(map); } System.out.println(); } @Override public void selectPersonByName() { throw new UnsupportedOperationException(); } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
As you see in above interface and implementation class we have created four method and made all four method transactional
- int insertUser(Person person) – Insert Person to the table
- void deletePerson(int personID) – Delete Person from table
- void selectAllPerson() – Select all person from table
- void selectPersonByName() – In this method intentionally we didn’t provide implementation and it throws UnsupportedOperationException instance which shows transactions have been created and after that it rolled back in response to UnsupportedOperationException instance that being thrown
Step 6: Create folder Spring inside src/main/resources/ and create file name application-config.xml then copy paste below content in it:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- DataSource to connect mysql database --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/JavaHonk"></property> <property name="username" value="root"></property> <property name="password" value="admin"></property> </bean> <!-- PlatformTransactionManager which are going to drive transaction--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- Transaction advice which set to the method using name and for actual implementation see <aop:config/> and <aop:advisor/> bean below --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="select*" read-only="true" /> <tx:method name="*" /> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- AOP (Aspect oriented programming) use tx:advive and ensure that any operation defined in the iPersonDAO interface are transactional--> <aop:config> <aop:pointcut id="iPersonDAO" expression="execution(* com.javahonk.dao.IPersonDAO.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="iPersonDAO"/> </aop:config> <!-- JdbcTemplate bean to execute query--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- Our main DAO class bean object which we are making transactional --> <bean id="personDAO" class="com.javahonk.dao.PersonDAO"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> </beans>
Important: application-config.xml is heart of declarative transaction management so lets discuss all tag defined above one by one:
- XML file beans tag:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
Beans tag is main tag works as container for all bean where all new bean will be defined and this makes spring as framework and it’s also called Inversion of Control (IOC) container. IOC manages java objects from instantiation to destruction using its bean factory.
Inside beans tag it uses xmlns (XML name space) attribute which is spring name space unique URLs
- DataSource bean to connect mysql database
<!-- DataSource to connect mysql database --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/JavaHonk"></property> <property name="username" value="root"></property> <property name="password" value="admin"></property> </bean>
Above bean tag is defined to connect data base and id is unique property to locate on run time. Properties are related to data base configuration. Note: Please don’t forget to change data base properties to match your database when using this demo application.
- DataSourceTransactionManager bean
<!-- PlatformTransactionManager which are going to drive transaction--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
To drive transaction we have registered DataSourceTransactionManager with data source and bean id transactionManager which actually going to drive transaction semantics.
- Transactional advice method configuration
<!-- Transaction advice which set to the method using name and for actual implementation see <aop:config/> and <aop:advisor/> bean below --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="select*" read-only="true" /> <tx:method name="*" /> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*"/> </tx:attributes> </tx:advice>
As we have created interface service object iPersonDAO and want to make all its method transactional. Transaction scenario we want to apply is encapsulated in </tx:advice> definition where “select*” and read-only=”true” means all method start with “select” will be executed in context of read only transaction all remaining method will be executed with default transaction semantics which is defined as <tx:method name “*”>. (In spring default transaction semantics is read-only=false means transaction will be executed in read write mode until define explicitly true as we did for method name starts with “select”). The ‘transaction-manager’ attribute of the tag has set with name of DataSourceTransactionManager bean which is going to drive transaction (Our case it is transactionManager bean)
- AOP use tx:advive that ensure that any operation defined in the iPersonDAO interface are transactional
<!-- AOP (Aspect oriented programming) use tx:advive and ensure that any operation defined in the iPersonDAO interface are transactional--> <aop:config> <aop:pointcut id="iPersonDAO" expression="execution(* com.javahonk.dao.IPersonDAO.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="iPersonDAO"/> </aop:config>
<aop:config/> definition confirms that transactional advice defined using ‘txAdvice’ bean will executes at appropriate points in program. First thing we have defined pointcut which matches execution of any operation (expression=”execution(* com.javahonk.dao.IPersonDAO.*(..))”/> defined in the IPersonDAO interface. We have associated pointcut with ‘txAdvice’ using an advisor with pointcut-ref indicates that execution of ‘iPersonDAO’ and advice defined by’txAdvice’ will be run.
- JdbcTemplate definiton
<!-- JdbcTemplate bean to execute query--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
JdbcTemplate bean defined and wired with data source to execute the query
- PersonDAO bean definition wired with JdbcTempalte
<!-- Our main DAO class bean object which we are making transactional --> <bean id="personDAO" class="com.javahonk.dao.PersonDAO"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean>
Our PersonDAO class which will use JdbcTemplate to execute the query in transactional semantics.
Step 7: Now create Person.java bean class inside package com.javahonk.di.bean and copy paste below code:
package com.javahonk.di.bean; import java.io.Serializable; public class Person implements Serializable{ private static final long serialVersionUID = 1L; private String FirstName; private String LastName; private String street; private String city; private String state; private String country; public Person(String firstName, String lastName, String street, String city, String state, String country) { super(); FirstName = firstName; LastName = lastName; this.street = street; this.city = city; this.state = state; this.country = country; } public String getFirstName() { return FirstName; } public void setFirstName(String firstName) { FirstName = firstName; } public String getLastName() { return LastName; } public void setLastName(String lastName) { LastName = lastName; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }
Step 8: Please add below dependencies in pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework.samples.service.service</groupId> <artifactId>SpringMaven</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <!-- Spring and Transactions --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>3.0.3.RELEASE</version> </dependency> <!-- MySql 5.5 Connector --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.29</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.4</version> </dependency> </dependencies> </project>
Step 9: Maven project create logger file automatically but in case it’s not created then create logback.xml inside src\main\resources folder and copy paste below code in it:
<!-- configuration file for LogBack (slf4J implementation) See here for more details: http://gordondickens.com/wordpress/2013/03/27/sawing-through-the-java-loggers/ --> <configuration scan="true" scanPeriod="30 seconds"> <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"> <resetJUL>true</resetJUL> </contextListener> <!-- To enable JMX Management --> <jmxConfigurator/> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-5level %logger{0} - %msg%n</pattern> </encoder> </appender> <!--<logger name="org.hibernate" level="debug"/> --> <!-- Uncomment and add your logger here: <logger name="org.springframework.samples.service.service" level="debug"/> --> <root level="info"> <appender-ref ref="console"/> </root> </configuration>
Step 10: Our all configuration is completed lastly create main class to test it. Create main class name: TestSpringJDBCTemplate.java inside com.javahonk package and copy paste below content in it:
package com.javahonk; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.javahonk.dao.IPersonDAO; import com.javahonk.di.bean.Person; public class TestSpringJDBCTemplate { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "spring\\application-config.xml"); IPersonDAO personDAO = applicationContext. getBean("personDAO", IPersonDAO.class); Person person = new Person("Java", "Honk", "John st.","NY", "NY", "USA"); //Insert user to the table 3 times: for (int i = 0; i < 3; i++) { personDAO.insertUser(person); } //Delete first person from table personDAO.deletePerson(1); //Select all inserted user from the table personDAO.selectAllPerson(); //Below method we are throwing exception intentionally //to check if transaction enabled personDAO.selectPersonByName(); applicationContext.close(); } }
Step 11: Our final project structure will look as below:
Step 12: Let’s test it. Right click main class TestSpringJDBCTemplate.java –> Run As –> Java Application. You will see record inserted deleted selected and throws exception on the console below:
That’s it spring declarative transaction management XML based completed successfully.
Download souce code : SpringDeclarativeTransaction