Now that we have covered some background material regarding transactions from a general J2EE perspective, it is time to look at what Spring has to offer. Before we go into detail about Spring's support for transaction management, let's just take a quick look at a brief example using programmatic transaction management with Spring's DataSourceTransactionManager.
The task that we need to wrap in a transaction is updating the price of all rows in the Price_Band table. We will update the price using the current price to determine the size of the increase. If there is an error while the updates are performed, the transaction should be rolled back.
This example starts with some setup work; we create a DataSource instance and also an instance of the DataSourceTransactionManager. Both these instances are passed into a new instance of the MassUpdate class. We could have done all this using Dependency Injection, instead of doing it in the main method of our example. The doUpdate method starts by creating the TransactionDefinition and then the transaction status is obtained by a call to getTransaction on the TransactionManager instance. This action starts the transaction.
The updates take place inside of a try/catch block to allow us to determine the outcome of the updates. If an exception is thrown, then we call rollback and re-throw the exception; otherwise everything went fine and we can call commit. The updates are performed using Spring's JDBC framework and you can read more about this in Chapter 5.
public class MassUpdate { DataSource dataSource; PlatformTransactionManager transactionManager; public static void main(String[] args) { // setup environment BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring"); ds.setUsername("spring"); ds.setPassword("t1cket"); DataSourceTransactionManager tm = new DataSourceTransactionManager(); tm.setDataSource(ds); // create update instance and set dependencies MassUpdate update = new MassUpdate(); update.setDataSource(ds); update.setTransactionManager(tm); // execute update.doUpdate(); } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void setTransactionManager( PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } public void doUpdate() { DefaultTransactionDefinition td = new DefaultTransactionDefinition( TransactionDefinition.PROPAGATION_REQUIRED); td.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); td.setTimeout(10); TransactionStatus status = transactionManager.getTransaction(td); try { updatePrices(); } catch (DataAccessException e) { transactionManager.rollback(status); throw e; } transactionManager.commit(status); } private void updatePrices() throws DataAccessException { UpdateQuery query = new UpdateQuery(); query.execute(); } private class UpdateQuery extends SqlQuery { // update logic omitted to keep this example brief – see code download // for the JDBC code used to update the table } }
This is an example of direct programmatic use of the DataSourceTransactionManager that works with a single DataSource without complicating things with a JTA implementation. Using a single resource transaction manager gives us the option of running the application standalone or deploying it to a standard Tomcat server with full transaction support. If we needed to deploy in an application server using JTA, then all we would have to do would be to replace the DataSourceTransactionManager with a JTATransactionManager. If we use Dependency Injection with a Spring application context, then thisis a one-minute change.