Oliver Gierke Archive About Tags

Hades 2.0.0.RC2 introduces transactional DAOs

22 June 2010

Our latest Hades release of the 2.0 branch is introducing transactionality of DAO instances as main new feature. Of course you can read up the reference documentation for some general guidelines but I want to use the chance to give a more detailed look into why we introduce this and how some details work.

These days, some kind of back-to-the-basics approach regarding architecture layering is widely accepted as best practices. Thus you don’t want to introduce a layer just because of some merely technical needs. A typical candidate layer for being obsolete is the service layer. Especially in web applications where you pretty much store entities that are equipped with rich behavior themselves the service layer is quite an artificial one that only demarcates transactions but then delegates to the underlying data access layer. Using Hades in such an application you had needed the additional transaction demarcation somewhere as it didn’t deal with transactions itself out of the box. So the only option to get rid of a service layer was to use e.g. @Transactional at a Spring MVC controller e.g. which is rather a sub-optimal solution.

So actually we were quite reluctant to the idea of applying transactions to Hades DAO instances as there were some crucial points to consider:

  1. Actually a DAO layer might not be at the right level of abstraction. Usually you define transactions on a more business related method than a plain save(…) or findByUsername(…). So if we enable transactions at DAO methods we have to make sure that they seamlessly can be expanded to more coarse grained methods and the DAOs take part in those transactions.
  2. If we apply transactions we don’t want to imply our transaction model to the user’s application. As Spring provides various means to define transactions, we don’t want to take that decision from the developer. So in case she wants to decide for a particular model, she has to be able to do so, no matter what we chose.
  3. Although the transactional behavior that we define for CRUD methods might be okay for 90% of the use cases there has to be a way to override the defaults and replace them by some custom one.
  4. As Spring 3.0 supports multiple transaction managers via @Transactional we have to make sure we participate in the right transaction depending on in which context the DAO is used.
  5. It has to be easy to define transaction configuration for DAO interfaces containing the finder methods.

We decided to use @Transactional annotations at the GenericJpaDao implementation class and activate annotation based transactions for the proxies explicitly through the GenericDaoFactoryBean. This means that the CRUD operations of the DAO instances will be transactional if called standalone or participate in a transaction in case there’s already one running for the current transaction manager (read up details on multiple transaction managers here). So it doesn’t matter if you declare your transactional boundaries via @Transactional, too, or use e.g. XML configuration or even TransactionTemplate. This gets us solutions for issues 1 and 2.

Issue 5 is quite easy to solve as you can simply annotate the finder methods of your DAO interface (or the interface itself) with @Transactional and transactions get applied as you are used to with Spring.

The actual tricky part is issue 3. The main idea here is to simply redeclare the CRUD method originally declared in GenericDao inside your concrete DAO interface and reconfigure transactions as you like. So supposed you want to get rid of the readOnly flag at readByPrimaryKey(…) set to true (as we chose to default the transaction configuration to, as this applies some performance optimizations on the persistence provider as well as the JDBC driver), you can do this as follows:

public interface UserDao extends GenericDao<User, Long> {

  @Override
  @Transactional
  User readByPrimaryKey(Long key);
}

Getting this to work needs some reflection magic as we of course still have to delegate the call to that method to the GenericJpaDao instance but according to the Java Reflection API it of course does only implement GenericDao.readByPrimaryKey(…) and not UserDao. readByPrimaryKey(…). Long story short, we can find out the method that has to be invoked so we can hand this reconfiguration model to the user and gain much flexibility on this side.

Issue 4 is not that interesting as we simply have to configure our internal TransactionInterceptor to lookup the appropriate PlatformTransactionManager instance in a lazy-loading-like fashion but that all happens under the covers and you don’t need to take care of this.

So let me quickly summarize what 2.0.0.RC2 brings you in regard of transactions:

blog comments powered by Disqus
Fork me on GitHub