April 14, 2004

Curious about Spring transaction definitions?

The interface org.springframework.transaction.TransactionDefinitionis the place for you. (Although it's useful to keep a copy of a J2EE book around since Spring uses its declaration semantics.) It's got the different propogation and isolation types and a short but effective sentence or two on what they mean.

Tonight, two days before an initial install, I finally got around to wrapping my DAO methods with transactions. Most of them are just single-use actions anyway so it didn't matter quite as much, but once you start assembling them into larger services (like 'createPayment' or 'voidTransaction') then you start feeling some pain.

Like everything else in Spring declaring transactions is pretty easy. First let's say you have a bean:

    <bean name="ledgerActionDao"
            class="com.optiron.dao.LedgerActionHibernateDAO">
        <property name="sessionFactory"><ref bean="mySessionFactory" /></property>
    </bean>

Now you want to tell Spring to manipulate transactions around certain methods -- the default transaction support is to say any exception thrown will trigger a rollback. To do this you create a proxy and point it at your bean:

    <bean name="ledgerActionDaoProxy"
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target"><ref bean="ledgerActionDao"/></property>
        <property name="transactionManager"><ref bean="transactionManager"/></property>
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
   </bean>

This is a little unrealistic (presumably we have some read-only methods that don't require a transaction). But it's still nifty... except for the fact that we have other objects already referring to 'ledgerActionDao'. I don't want to point them all at the proxy now because I'll surely miss something.

No problem. Just swap the names:

    <bean name="ledgerActionDaoTarget"
            class="com.optiron.dao.LedgerActionHibernateDAO">
...
    <bean name="ledgerActionDao"
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target"><ref bean="ledgerActionDaoTarget"/></property>

And since the DAOs are all generated I took advantage of Spring's flexibility by declaring a set of patterns to match against all my DAOs instead of listing the methods for each one:

    <bean name="daoTxAttributes"
    class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
        <property name="properties">
          <props>
            <prop key="find*">PROPAGATION_NOT_SUPPORTED</prop>
            <prop key="create*">PROPAGATION_REQUIRED</prop>
            <prop key="mark*">PROPAGATION_REQUIRED</prop>
            <prop key="update*">PROPAGATION_REQUIRED</prop>
          </props>
        </property>
    </bean>

And then you just reference it in the transaction declaration for each of your DAOs:

    <bean name="ledgerActionDao"
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target"><ref bean="ledgerActionDaoTarget"/></property>
        <property name="transactionAttributeSource"><ref bean="daoTxAttributes" /></property>

This is good stuff... BTW, if you haven't seen it yet this presentation from Eduardo Issao Ito is a fantastic overview of Spring with plenty of examples throughout.

UPDATE: Be careful with PROPAGATION_NOT_SUPPORTED -- if you call such a method within a transaction it won't be able to see the results of modifications within that transaction. (I had a nice "select is broken" moment there...) Most of the time you'll probably want PROPAGATION_SUPPORTS instead.

Next: Idealism and impatience
Previous: Fear of teenage violence run amok