Transaction Management for JPox with Spring.


[tweetmeme source=”gosub3000”]
I was on the edge of writing some unkind things about JPox and Spring this week, as I dealt with the frustration of getting Spring to manage transactions for the JPox persistence code I was writing.

The issue I was dealing with, was the persistence of a org.springframework.orm.jdo.JdoUsageException. I decided to simplify, and I downloaded the JPox JDO Tutorial, and decided to reimplement it using Spring.

Read on for the cause, and solution to my problem.

Soon after I began, I had a system reproducing the error I was experiencing with my other code. Progress of a sort I thought. Through I was becoming increasingly frustrated at the continued appearance of :

Exception in thread "main" org.springframework.orm.jdo.JdoUsageException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true'; nested exception is org.jpox.jdo.exceptions.TransactionNotActiveException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true'
at org.springframework.orm.jdo.PersistenceManagerFactoryUtils.convertJdoAccessException(PersistenceManagerFactoryUtils.java:239)
at org.springframework.orm.jdo.DefaultJdoDialect.translateException(DefaultJdoDialect.java:230)
at org.springframework.orm.jdo.JdoAccessor.convertJdoAccessException(JdoAccessor.java:165)
at org.springframework.orm.jdo.JdoTemplate.execute(JdoTemplate.java:209)
at org.springframework.orm.jdo.JdoTemplate.makePersistent(JdoTemplate.java:331)
at org.jpox.tutorial.dao.BookDao.storeBook(BookDao.java:23)
at org.jpox.tutorial.jdo.Main.main(Main.java:61)
Caused by: org.jpox.jdo.exceptions.TransactionNotActiveException: Transaction is not active. You either need to define a transaction around this, or run your PersistenceManagerFactory with 'NontransactionalRead' and 'NontransactionalWrite' set to 'true'
at org.jpox.jdo.AbstractPersistenceManager.assertWritable(AbstractPersistenceManager.java:2096)
at org.jpox.jdo.AbstractPersistenceManager.makePersistent(AbstractPersistenceManager.java:657)
at org.springframework.orm.jdo.JdoTemplate$9.doInJdo(JdoTemplate.java:333)
at org.springframework.orm.jdo.JdoTemplate.execute(JdoTemplate.java:204)
... 3 more

After continuing to search blogs, wikis and forums, I couldn’t find why my code was failing. I believed the code I was using was correct, and every tweak I made didn’t help. After spending days scouring blogs and forums, I was convinced that my code was good, and there was either an error in Spring, or JPox.

The design of the system, had me create data access objects (DAOs) for both Book and Product classes as subclasses of org.springframework.orm.jdo.support.JdoDaoSupport. To manage the transaction wrapping, I had specified both these classes to a org.springframework.transaction.interceptor.TransactionProxyFactoryBean instance within my application context configuration file. I was then getting references to the spring managed DAO objects, and calling their methods, expecting Spring to handle the transaction management. The salient parts of my application context are here:


<!-- Book DAO -->

<bean id="bookDAO" class="org.jpox.tutorial.dao.BookDao">
    <property name="persistenceManagerFactory">
        <ref local="pmf" />
    </property>
</bean>

<!-- Transactional proxy for the book DAO -->
<bean id="BookTxnProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="jdoTransactionManager"/>
    </property>
    <property name="target">
        <ref local="bookDAO"/>
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="store*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

<!-- Product DAO -->
<bean id="productDAO" class="org.jpox.tutorial.dao.ProductDao">
    <property name="persistenceManagerFactory">
        <ref local="pmf" />
    </property>
</bean>

<!-- Transactional proxy for the product DAO -->
<bean id="ProductTxnProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="jdoTransactionManager"/>
    </property>
    <property name="target">
        <ref local="productDAO"/>
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="store*">PROPAGATION_REQUIRED</prop>
            <prop key="delete*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

The application code I used to persist a book for example, looked like:


BookDao bookDao = (BookDao) context.getBean("bookDAO");
ProductDao prodDao = (ProductDao) context.getBean("productDAO");
// ...
bookDao.storeBook(book);
prodDao.storeProduct(product);

This resulted in the offending exception.To fix the situation, I had to first realise that the TransactionProxyFactoryBeans I was using, didn’t do their magic in the background automatically, and my application code would have to reference them in some way. After researching the javadoc for this class, I realised the correct configuration would be to introduce Java interfaces specifying the public interfaces of both BookDao and ProductDao, which I didn’t previously have. I then changed the names of these classes to BookDaoImpl, and ProductDaoImpl respectively, with the interfaces named BookDao and ProductDao.

I could then specify the proxy interfaces to the TransactionProxyFactoryBeans, and reorganise the spring configuration file, so that instead of getting references to the BookDaoImpl for example, I got a reference to the proxy object created by the TransactionProxyFactoryBean on it’s behalf. Now that the Proxy object knows which interfaces to mimic, calling the proxied methods was just like calling methods on the original object, but now the proxied object was handling the transaction management correctly.

My application context configuration file now looked like:

<!-- Book DAO -->
<bean id="bookDaoTarget" class="org.jpox.tutorial.dao.BookDaoImpl">
    <property name="persistenceManagerFactory">
        <ref local="pmf" />
    </property>
</bean>

<!-- Transactional proxy for the book DAO -->
<bean id="bookDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="proxyInterfaces">
        <list>
            <value>org.jpox.tutorial.dao.BookDao</value>
        </list>
    </property>
    <property name="transactionManager">
        <ref local="jdoTransactionManager" />
    </property>
    <property name="target">
        <ref local="bookDaoTarget" />
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="store*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

<!-- Product DAO -->
<bean id="productDaoTarget" class="org.jpox.tutorial.dao.ProductDaoImpl">
    <property name="persistenceManagerFactory">
        <ref local="pmf" />
    </property>
</bean>

<!-- Transactional proxy for the product DAO -->
<bean id="productDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="proxyInterfaces">
        <list>
            <value>org.jpox.tutorial.dao.ProductDao</value>
        </list>
     </property>
     <property name="transactionManager">
        <ref local="jdoTransactionManager" />
     </property>
     <property name="target">
        <ref local="productDaoTarget" />
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="store*">PROPAGATION_REQUIRED</prop>
            <prop key="delete*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

The application code is changed to :

BookDao bookDao = (BookDao) context.getBean("bookDao");
ProductDao prodDao = (ProductDao) context.getBean("productDao");
//...
bookDao.storeBook(book);
prodDao.storeProduct(product);

The difference here is that bookDao and prodDao are references to proxy objects created by the TransactionProxyFactoryBean, though they support the BookDao and ProductDao interfaces, so casting them as such does not cause any issues. Calling storeBook and storeProduct on them causes the proxy object to wrap transactions around the calls to the proxied BookDaoImpl and ProductDaoImpl classes, as specified in the application context configuration file.

Advertisements

Tags: , , ,

2 responses to “Transaction Management for JPox with Spring.”

  1. tim says :

    what plugin are you using to get your xml code to show up formatted nicely and colorized?

    Thanks for the article.

  2. ccollins says :

    Hi Tim,
    I use the “sourcecode” shortcode provided by wordpress.com for this. Details are here:
    http://faq.wordpress.com/2007/09/03/how-do-i-post-source-code/

    -Chris

%d bloggers like this: