Pages

Monday, 31 March 2014

Lesson 06 - Spring Transaction Management & DAO Support

Note: In this chapter I don't want to write java code that hits the database and saves/updates in database tables. Wherever I intent to write db code I keep System.out.print statements to resemble that. Since we did not discuss how database interaction happens in spring yet, I had to follow this way.

Spring framework provides consistent abstraction for transaction management (tx mgmt) which has following advantages:

  • consistent programming model across different transaction APIs such as JDBC, Hibernate, JDO, JTA..
  • Support for both declarative and programmatic transaction management 
  • excellent integration with spring's data access abstraction
Before start understanding spring tx mgmt we need to understand difference between global and local transactions. Global transactions enables us to work with multiple transaction resources such as databases, message queues. Usually application server manages global tx through JTA which usually needs JNDI.EJB's CMT (Container managed transaction) enables to use global transaction with out the need of transaction related JNDI lookups but use of EJB itself needs some association with JNDI.Also this works, if user willing to write business logic in EJBs (that itself is a big no due to its complexity). When it comes to local transaction on contrary are resource specific such as JDBC connection.They are easy to use but we cant use them across multiple transactional resources. i.e code managing transactions using jdbc connection can not run in JTA global transaction. Since application server is not involved in local transactions it cant manage correctness across multiple transactional resources. However most of the applications are using local transactions only.. With Spring tx management you get rid of all these issues as your programming is consistent across environments. You write code once and it can be used in different tx strategies in diff environments.

Spring's PlatformTransactionManager

The most important interface of whole spring's tx mgmt stack is PlatformTransactionManager (PTM) interface. Its definition is like below:

public interface PlatformTransactionManager {

  TransactionStatus getTransaction(TransactionDefinition definition)
    throws TransactionException;

  void commit(TransactionStatus status) throws TransactionException;

  void rollback(TransactionStatus status) throws TransactionException;
}

It is basically SPI (i.e it need to be developed by APIs). If you see this has no binding to JNDI and any implementations of PTM can be marked as usual beans in spring container. Exceptions thrown above "TransactionException" is an unchecked one. So developers are not forced to handle them. Cool is nt it?. getTransaction() method returns a TransactionStatus object depending on TransactionDefinition parameter. Returned object represents a new transaction or matching transaction in current call.

TransactionDefinition & TransactionStatus

Usually transaction definition specifies isolation,propagation,time-out and read-only status of the given transaction. All these are usual transaction parameters. While TransactionStatus defines and provides a way to query the transaction in a way to check whether transaction is a new one or is that completed or does it have any save point etc...  Irrespective of whether you choose declarative/programmatic approach, implementing the PTM is crucial Usually we pick one of the out of the box implementations and we provide information about environment in which they (PTM) work such as JDBC,JTA, Hibernate etc.. Below code is based on simple JDBC related transaction manager with my own platform transaction manager implementation to show how you can also implement transaction managers. We comes to actual fun stuff after this example. For first timers I am showing my pom.xml as well to get related jars.




Now In this example our custom PTM accepting datasoruce (since its based on JDBC). So if you choose to use hibernate consider using out of the box PTM called HibernateTransactionManager which accepts LocalSessionFactroyBean (like our hibernate's session factory bean) as parameter. Remember if you are using JTA no need to pass any these sort of datasource/session factory beans since JTA completely depends on JEE container global transaction infrastructure. If you are using JTS in JEE container then consider using "container datasource" obtained through JNDI like below:

  <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myds"/>
  <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
Similarly if you are using hibernate and JEE container managed JTA transaction, even then your transaction manager definition is like above only because if you sue JTA your transaction manager definition will look the same irrespective of what technology you prefer to use (like be it JDBC, Hibernate, JPA or any others). This is due to the fact that JTA transactions are global transactions, which can enlist any transactional resource.

So far we have seen how PTMs and their resources that need to be synchronised to for example DataSourceTransactionManager to a JDBC DataSource, HibernateTransactionManager to a HibernateSessionFactory) now lets see how these resources are managed. Classes such as DataSourceUtils (for JDBC), EntityManagerFactoryUtils (for JPA), SessionFactoryUtils (for Hibernate) so on exist at a lower level to manage (like resource cleaning, closing connections etc..) and spring suggests to use these classes so that framework managed instances are obtained, transactions are synchronised and exceptions are properly mapped to consistent api.

Declarative Transaction Management

Most of the developers choose declarative tx mgmt due to the fact its non-intrusive nature to the application code and less impact on application code. Usually you could use tx mgmt code with the help of AOP, or with help of boiler plate code with no usage of AOP. Spring's tx mgmt looks like EJB's CMT where as we could provide tx mgmt details at the individual method level. Following are few differences:

  • unlike EBJ's CMT which is ties to JAT, Spring's tx mgmt works for JTA/JDBC/JDO/Hibernate etc... just by tweaking few configuration files, same code works in all envs.
  • The tx mgmt can be applied to any class not necessarily to be a EJB class.
  • Spring has declarative (and programatic) rollbacks which EJB does not have.
  • In spring you could customise tx behaviour with the help of AOP,i.e applying some advice in case of rollback. In EJB CMT you cant influence it except setRollbackOnly() method.
  • Spring does nt support, propagation of transaction contexts, if this is what you need, consider EJB.
Most important thing to understand is, spring declarative tx support is enabled by AOP proxies and transactional advice is driven by meta data (xml/annotations). So this AOP +tx meta data yields, TransactionalInterceptor in conjunction with PTM implementation to rive tx around method invocations.

Let us build a simple example to understand the whole process.



From the above example what we need to understand is, 

  • Transaction capabilities are applied to EmployementService with the help of <aop:config> and <tx:advice> elements. <tx:advice> reads as "printEmployee" has read-only nature and other methods has all read-write nature of transaction. <tx:method has few self explanatory attributes to give complete information about how transaction nature is for the given method.
  • <tx:advice accepts a ptm whose name is given under attribute called "transaction-manager"
  • <aop:config> ensures that transactional advice given by <tx:advice> bean executes at the appropriate points mentioned by pointcut.
  • I have used log4.properties to show whats happening underlying the scenes. Here above config will create a transactional proxy with transactional advice (as specified in tx:advice) so when appropriate method is called on proxy, transaction is created/read-only depending on how you mentioned in <tx:advice>
  • Hence you could see all debug statements when creating a proxy, opening a db connection and closing it etc.. all these boilerplate is now away from us. If you choose not to use AOP then you could employe TransactionTemplates (we see in next section) as well.
  • Once you understand above settings, whole reminder of the chapter is very easy.

Rolling back a declarative transaction

we will see here how to rollback on an occurrence of exception. Here we see how to rollback when Exception from your code is thrown in the context of transaction. Spring tx infrastructure catch and unhandled exception and it bubbles up and takes a decision whether or not make the transaction to rollback. By default spring only rollback in case of Unchecked exceptions but you configure to rollback on checked/unchecked exceptions/application-specific Exceptions. Similarly you could mention for what sort of exception you DO not need to rollback


<tx:advice id="txadvice1234" transaction-manager="transactionManager">
<tx:attributes>
    <tx:method name="updateEmployee" read-only="true" rollback-for="UpdateFailureException" no-rollback-for="UnabletoPrintToFileException"/> 
    <tx:method name="*" />
</tx:attributes>

</tx:advice>

however you could do the same thing as well pragmatically but we don't want our code to be tightly coupled with spring,
public void doSomeThing() {
  try {
    // some business logic...
  } catch (NoProductInStockException ex) {
    // rollback programmatically
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  }
}

So you could have different <tx:advice> elements with different methods with different transaction semantics plugged into different <aop:advisors>. Its perfectly valid. Because <aop:config> can hold n number of <aop:advisor> elements with each one carrying different "advice-ref" attributes.

Understanding <tx:advice>

following are the default setting, (if you are using Spring tool suite, by this time you may be aware of it)
  • Propagation setting is REQUIRED.
  • Isolation level is DEFAULT.
  • Transaction is read/write.
  • Transaction timeout defaults to the default timeout of the underlying transaction system, or none if timeouts are not supported.
  • Any RuntimeException triggers rollback, and any checked Exception does not.
but you are free to change these values by setting various attributes of <tx:method>

Introduction to @Transactional

Here comes the best part of the spring tx mgmt using annotations. you could place this annotation before an interface/method on interface/public method on a class. But just keeping this does nt make it transactional infrastructure to use. Basically, its a hint to runtime spring tx mgmt. But actually following lines of config enables the spring transaction behaviour. Alternatively if you are using java based config, keep @EnableTransactionManagement for config class. 

<tx:annotation-driven transaction-manager="transactionManager"/>

Infact spring supports keeping @Transactional to concrete classes rather interfaces. Certainly you can place it on interface/interface methods but that only works as per your expectation only if you are using interfaced based proxies. The fact that Java annotations are not inherited from interfaces means, that if you are using class-based proxies then object weaving does not happen correctly and tx mgmt would fail. @EnableTransactionManagement and <tx:annotation-driven/> only looks for @Transactional on beans in the same application context they are defined in. If @Transactional settings are defined at class, method level then method level takes precedence. The default settings for @Transactional is exactly same like <tx:advice> settings (see above). But we could change it. See below example for how to do that.


So in second case it issues rollback due to power failure and have overwritten class level settings. Most of the applications uses only one transactionmanager but if we have many, consider @Transactional ("name_of_the_ptm") format to specify it. While setting transactional attributes, propagation plays major role.

Advising Transactional Operations

If you want to advice transactional operations i.e advice on transactional code then consider implementing advice class to implement Ordered interface. Then keep the order of it less than transactional advice. The way you set order for transactional advice is explained below example (As an attribute to tx:annotation-driven). In below example we are setting some advice to run before real db logic is about to be executed.


this shows that before DB code our advice is running...

Programmatic Transaction Management

We have two ways to achieve transaction management programatically. They are using "TransactionTemplate" or using "PlatformTransactionManager implementation directly". We see both versions very soon. Spring suggests us to use template approach (its like call-back approach). In template approach you write the main business logic and all boiler plate code will be provided by template (since its same all the time - mostly). As an application developer you implement "TransactionCallback" to the "execute" method of "TransactionTemplate". Since instances of "TransactionTemplate" are thread safe. So if you want to have transaction template with different settings, consider creating two instances of it. Below example shows the usage of TransactionTemplate:

Now lets move to PlatformTransactionManager. In case of PTM it is pretty straight forward to use the implementation and call either commit/rollback methods. Below example explains it. It is suggested to use Programmatic approach when you have limited number of transaction operations. In case of many tx operations, always prefer declarative approach. 

DAO Support

DAO support in spring allows to write consistent code which makes it easy to work with various data access technologies such as JDBC, Hibernate, JPA, JDO etc..With this we can switch between technologies without worrying about technology specific exceptions to be caught.Spring provides a special exception hierarchy which is wraps most of the data access exceptions and throws technology agnostic exceptions. All these exception wraps original exception so you never miss anything and moreover they are unchecked so you no need to handle them.
 
Above diagram shows exception hierarchy from spring which is not tied to any specific data access technology. We need to use @Repositoy annotation on DAOs to ensure exception translation will happen.This as well registers DAOs as spring components while scanning for components. In next chapter (big one) we see how to handle database access with spring + JDBC. Then we dive into ORM world with hiberanate as data access technology.




Previous Next

No comments:

Post a Comment