Spring supports the standard JDO 2.0 and 2.1 APIs as data access
strategy, following the same style as the Hibernate support. The
corresponding integration classes reside in the
org.springframework.orm.jdo
package.
Spring provides a
LocalPersistenceManagerFactoryBean
class that
allows you to define a local JDO
PersistenceManagerFactory
within a Spring
application context:
<beans> <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean"> <property name="configLocation" value="classpath:kodo.properties"/> </bean> </beans>
Alternatively, you can set up a
PersistenceManagerFactory
through direct
instantiation of a
PersistenceManagerFactory
implementation
class. A JDO PersistenceManagerFactory
implementation class follows the JavaBeans pattern, just like a JDBC
DataSource
implementation class, which is
a natural fit for a configuration that uses Spring. This setup style
usually supports a Spring-defined JDBC
DataSource
, passed into the
connectionFactory
property. For example, for the
open source JDO implementation DataNucleus (formerly JPOX) (http://www.datanucleus.org/),
this is the XML configuration of the
PersistenceManagerFactory
implementation:
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="myPmf" class="org.datanucleus.jdo.JDOPersistenceManagerFactory" destroy-method="close"> <property name="connectionFactory" ref="dataSource"/> <property name="nontransactionalRead" value="true"/> </bean> </beans>
You can also set up JDO
PersistenceManagerFactory
in the JNDI
environment of a Java EE application server, usually through the JCA
connector provided by the particular JDO implementation. Spring's
standard JndiObjectFactoryBean /
can be used to
retrieve and expose such a
<jee:jndi-lookup>
PersistenceManagerFactory
. However,
outside an EJB context, no real benefit exists in holding the
PersistenceManagerFactory
in JNDI: only
choose such a setup for a good reason. See Section 13.3.6, “Comparing container-managed and locally defined resources” for a discussion; the arguments
there apply to JDO as well.
DAOs can also be written directly against plain JDO API, without
any Spring dependencies, by using an injected
PersistenceManagerFactory
. The following
is an example of a corresponding DAO implementation:
public class ProductDaoImpl implements ProductDao { private PersistenceManagerFactory persistenceManagerFactory; public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { this.persistenceManagerFactory = pmf; } public Collection loadProductsByCategory(String category) { PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); try { Query query = pm.newQuery(Product.class, "category = pCategory"); query.declareParameters("String pCategory"); return query.execute(category); } finally { pm.close(); } } }
Because the above DAO follows the dependency injection pattern, it
fits nicely into a Spring container, just as it would if coded against
Spring's JdoTemplate
:
<beans> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmf"/> </bean> </beans>
The main problem with such DAOs is that they always get a new
PersistenceManager
from the factory. To
access a Spring-managed transactional
PersistenceManager
, define a
TransactionAwarePersistenceManagerFactoryProxy
(as included in Spring) in front of your target
PersistenceManagerFactory
, then passing a
reference to that proxy into your DAOs as in the following example:
<beans> <bean id="myPmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> <property name="targetPersistenceManagerFactory" ref="myPmf"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmfProxy"/> </bean> </beans>
Your data access code will receive a transactional
PersistenceManager
(if any) from the
PersistenceManagerFactory.getPersistenceManager()
method that it calls. The latter method call goes through the proxy,
which first checks for a current transactional
PersistenceManager
before getting a new
one from the factory. Any close()
calls on the
PersistenceManager
are ignored in case of
a transactional
PersistenceManager
.
If your data access code always runs within an active transaction
(or at least within active transaction synchronization), it is safe to
omit the PersistenceManager.close()
call and
thus the entire finally
block, which you might do to
keep your DAO implementations concise:
public class ProductDaoImpl implements ProductDao { private PersistenceManagerFactory persistenceManagerFactory; public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { this.persistenceManagerFactory = pmf; } public Collection loadProductsByCategory(String category) { PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager(); Query query = pm.newQuery(Product.class, "category = pCategory"); query.declareParameters("String pCategory"); return query.execute(category); } }
With such DAOs that rely on active transactions, it is recommended
that you enforce active transactions through turning off
TransactionAwarePersistenceManagerFactoryProxy
's
allowCreate
flag:
<beans> <bean id="myPmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy"> <property name="targetPersistenceManagerFactory" ref="myPmf"/> <property name="allowCreate" value="false"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="persistenceManagerFactory" ref="myPmfProxy"/> </bean> </beans>
The main advantage of this DAO style is that it depends on JDO API only; no import of any Spring class is required. This is of course appealing from a non-invasiveness perspective, and might feel more natural to JDO developers.
However, the DAO throws plain
JDOException
(which is unchecked, so does
not have to be declared or caught), which means that callers can only
treat exceptions as fatal, unless you want to depend on JDO's own
exception structure. Catching specific causes such as an optimistic
locking failure is not possible without tying the caller to the
implementation strategy. This trade off might be acceptable to
applications that are strongly JDO-based and/or do not need any special
exception treatment.
In summary, you can DAOs based on the plain JDO API, and they can
still participate in Spring-managed transactions. This strategy might
appeal to you if you are already familiar with JDO. However, such DAOs
throw plain JDOException
, and you would
have to convert explicitly to Spring's
DataAccessException
(if desired).
Note | |
---|---|
You are strongly encouraged to read Section 10.5, “Declarative transaction management” if you have not done so, to get a more detailed coverage of Spring's declarative transaction support. |
To execute service operations within transactions, you can use Spring's common declarative transaction facilities. For example:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager"> <property name="persistenceManagerFactory" ref="myPmf"/> </bean> <bean id="myProductService" class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> </bean> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="increasePrice*" propagation="REQUIRED"/> <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> </aop:config> </beans>
JDO requires an active transaction to modify a persistent object.
The non-transactional flush concept does not exist in JDO, in contrast
to Hibernate. For this reason, you need to set up the chosen JDO
implementation for a specific environment. Specifically, you need to set
it up explicitly for JTA synchronization, to detect an active JTA
transaction itself. This is not necessary for local transactions as
performed by Spring's JdoTransactionManager
, but
it is necessary to participate in JTA transactions, whether driven by
Spring's JtaTransactionManager
or by EJB CMT and
plain JTA.
JdoTransactionManager
is capable of
exposing a JDO transaction to JDBC access code that accesses the same
JDBC DataSource
, provided that the
registered JdoDialect
supports retrieval of the
underlying JDBC Connection
. This is the
case for JDBC-based JDO 2.0 implementations by default.
As an advanced feature, both JdoTemplate
and interfacename
support a custom
JdoDialect
, to be passed into the
jdoDialect
bean property. In this scenario, the DAOs will
not receive a PersistenceManagerFactory
reference but rather a full JdoTemplate
instance
(for example, passed into the jdo
Template
property of JdoDaoSupport
. Using a
JdoDialect
implementation, you can enable
advanced features supported by Spring, usually in a vendor-specific
manner:
Applying specific transaction semantics such as custom isolation level or transaction timeout
Retrieving the transactional JDBC
Connection
for exposure to JDBC-based
DAOs
Applying query timeouts, which are automatically calculated from Spring-managed transaction timeouts
Eagerly flushing a
PersistenceManager,
to make
transactional changes visible to JDBC-based data access code
Advanced translation of JDOExceptions
to
Spring DataAccessExceptions
See the JdoDialect
Javadoc for more details
on its operations and how to use them within Spring's JDO
support.