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:
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.@Transactional
we have to make sure we participate in the right transaction depending on in which context the DAO is used.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:
) with appropriate defaults (
readOnly` flag set to true on read-only methods) that automatically take part in more coarse grained transactions if necessary@Transactional
for your concrete DAO interfaces