使用Spring集成JDO

Spring支持标准的JDO 1.0/2.0 API作为数据访问策略,同样遵循类似于Spring对Hibernate的支持风格。对应的支持与整合类位于 org.springframework.orm.jdo 包中。

建立PersistenceManagerFactory
Spring提供了一个 LocalPersistenceManagerFactoryBean 类,允许你通过Spring的application context来定义一个本地的JDO PersistenceManagerFactory:

<beans>

<bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
<property name="configLocation" value="classpath:kodo.properties"/>
</bean>

</beans>
作为可选方案,PersistenceManagerFactory 也可以通过直接实例化一个 PersistenceManagerFactory 的实现类得到。 一个JDO PersistenceManagerFactory 的实现类遵循JavaBeans的模式,它非常像一个JDBC DataSource 的实现类,这使得它天然的非常适合作为一个Spring的bean定义。 这种创建风格通常支持一个Spring定义的JDBC DataSource,将它传入到对应的实现类的connectionFactory的属性中进行bean的创建。具体的例子参见开源的JDO实现JPOX(http://www.jpox.org):

<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.jpox.PersistenceManagerFactoryImpl" destroy-method="close">
<property name="connectionFactory" ref="dataSource"/>
<property name="nontransactionalRead" value="true"/>
</bean>

</beans>
一个JDO的 PersistenceManagerFactory 能够同样在一个J2EE应用服务器的JNDI环境下被创建。 这通常由特定的JDO实现所提供的JCA连接器来完成。Spring标准的 JndiObjectFactoryBean 也能够被用来获取和暴露这个 PersistenceManagerFactory。 当然,通常在一个EJB环境之外,在JNDI中持有 PersistenceManagerFactory 的实例没有什么特别吸引人的好处,因而我们一般只在有非常充足的理由时选择这种建立方式。 请参看Hibernate章节中“容器资源 vs 本地资源”这一节的讨论,那里的讨论同样适用于JDO。

JdoTemplate和JdoDaoSupport
每一个基于JDO的DAO类都需要通过IoC来注入一个 PersistenceManagerFactory。 这样的DAO类可以在 PersistenceManagerFactory 的帮助下来操作原生的JDO API进行编程, 但是通常来说我们更愿意使用Spring提供的 JdoTemplate:

<beans>

<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="persistenceManagerFactory" ref="myPmf"/>
</bean>

</beans>
public class ProductDaoImpl implements ProductDao {

private JdoTemplate jdoTemplate;

public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
this.jdoTemplate = new JdoTemplate(pmf);
}

public Collection loadProductsByCategory(final String category) throws DataAccessException {
return (Collection) this.jdoTemplate.execute(new JdoCallback() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newQuery(Product.class, "category = pCategory");
query.declareParameters("String pCategory");
List result = query.execute(category);
// do some further stuff with the result list
return result;
}
});
}
}
一个回调的实现能够有效地在任何JDO数据访问中使用。 JdoTemplate 会确保当前的 PersistenceManager 对象的正确打开和关闭,并自动参与到事务管理中去。 Template实例不仅是线程安全的,同时它也是可重用的,因而他们可以作为外部对象的实例变量而被持有。 对于那些简单的诸如 find、load、 makePersistent 或者 delete 操作的调用, JdoTemplate 提供可选择的快捷函数来替换这种回调的实现。 不仅如此,Spring还提供了一个简便的 JdoDaoSupport 基类, 这个类提供了 setPersistenceManagerFactory(..) 方法来接受一个 PersistenceManagerFactory 对象, 同时提供了 getPersistenceManagerFactory() 和 getJdoTemplate() 给子类使用。 综合了这些,对于那些典型的业务需求,就有了一个非常简单的DAO实现:

public class ProductDaoImpl extends JdoDaoSupport implements ProductDao {

public Collection loadProductsByCategory(String category) throws DataAccessException {
return getJdoTemplate().find(
Product.class, "category = pCategory", "String category", new Object[] {category});
}
}
作为不使用Spring的 JdoTemplate 来实现DAO的替代解决方案,你依然可以通过在原生JDO API的级别对那些基于Spring的DAO进行编程,此时你必须明确地打开和关闭一个 PersistenceManager。 正如在相应的Hibernate章节描述的一样,这种做法的主要优点在于你的数据访问代码可以在整个过程中抛出checked exceptions。JdoDaoSupport 为这种情况提供了多种函数支持, 包括获取和释放一个具备事务管理功能的 PersistenceManager 和相关的异常转化。

基于原生的JDO API实现DAO
我们可以直接操作JDO API来实现DAO,通过直接使用一个注入的 PersistenceManagerFactory,而无需对Spring产生的任何依赖。 一个相应的DAO实现看上去就像下面这样:

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();
}
}
}
上面我们所列出的DAO完全遵循IoC,它如同使用Spring的 JdoTemplate 进行编码那样,非常适合在Spring容器中进行配置:

<beans>

<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="persistenceManagerFactory" ref="myPmf"/>
</bean>

</beans>
这类DAO的主要问题在于他们每次总是从工厂中得到一个新的 PersistenceManager 实例。为了依然能够访问受到Spring管理的、具备事务管理功能的 PersistenceManager, 不妨考虑一下在目标 PersistenceManagerFactory 之前, 定义一个 TransactionAwarePersistenceManagerFactoryProxy(Spring已经提供), 然后将这个代理注入到你的DAO中去。

<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>
这样,你的数据访问代码就可以通过 PersistenceManagerFactory.getPersistenceManager() 方法得到一个具备事务管理功能的 PersistenceManager。 而这一方法将通过代理类的处理:在从工厂获取一个新的 PersistenceManager 实例之前检查一下当前具备事务管理功能的 PersistenceManager, 如果这是一个具备事务管理功能的 PersistenceManager, close() 调用在此时将被忽略。

如果你的数据访问代码总是在一个处于活跃状态的事务中执行(或者至少在一个活跃的事务同步中), 那么你能够非常安全地忽略 PersistenceManager.close() 的调用和整个 finally 块的代码。这将使你的DAO实现变得比较简洁:

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);
}
}
对于这种依赖于活跃事务的DAO,比较推荐的做法是将 TransactionAwarePersistenceManagerFactoryProxy 中的"allowCreate"标志关闭,从而强制活跃事务。

<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>
这种DAO访问方式的主要优势在于它仅仅依赖于JDO API本身而无需引入任何的Spring的类。 从无入侵性的角度来看,这一点非常吸引人。同时,对于JDO开发人员来说也更自然。

然而,这样的DAO访问方式会抛出 JDOException, 这是一个无需声明或捕获的unchecked exception。这意味着,DAO的调用者只能以普通的错误来处理这些异常, 除非完全依赖JDO自身的异常体系。因而,除非你将DAO的调用者绑定到具体的实现策略上去, 否则你将无法捕获特定的异常原因(诸如乐观锁异常)。这种折中平衡或许可以被接受, 如果你的应用完全基于JDO或者无需进行特殊的异常处理。

总体来说,DAO可以基于JDO的原生API实现,同时,它依旧需要能够参与到Spring的事务管理中。 这对于那些已经对JDO非常熟悉的人来说很有吸引力,因为这种方式更加自然。 不过,这种DAO将抛出 JDOException,因而, 如果有必要的话需要明确地去做由 JDOException 到Spring的 DataAccessException 的转化。

事务管理
将事务管理纳入到Service操作的执行中,你可以使用Spring通用的声明式的事务管理功能, 参加下面的例子:

<?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-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.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要求你必须在一个活跃的事务中修改一个持久化对象。 与Hibernate相比,在JDO中并没有一种类似脱离事务刷出(non-transactional flush)的概念。 基于这种原因,你所选择的JDO实现需要被建立在特定的环境中,尤其是它需要为JTA同步做明确的创建, 由此来自行检测一个JTA事务。这一点对于本地事务不是必要的,由于它已经被Spring的 JdoTransactionManager 处理,但是对于需要参与到JTA事务中的情况,是必须的 (无论是由Spring的 JtaTransactionManager、EJB CMT或者普通的JTA所驱动的事务)。

JdoTransactionManager 能够将一个JDO事务暴露给访问相同的JDBC DataSource 的JDBC访问代码。前提条件是,被注册的 JdoDialect 能够支持获取底层的JDBC Connection。 这一功能默认被基于JDBC的JDO 2.0 实现。对于JDO 1.0的实现,必须注册一个用户自定义的 JdoDialect。具体参见下一节有关 JdoDialect 的机制。

JdoDialect
作为高级特性,JdoTemplate 和 interfacename 都支持一个用户自定义的 JdoDialect 作为“jdoDialect”的bean属性进行注入。 在这种情况下,DAO将不再接收 PersistenceManagerFactory 的引用作为参数, 而是接收一个完整的 JdoTemplate 实例(也就是将它注入到 JdoDaoSupport 的"jdoTemplate"属性中去)。一个 JdoDialect 实现能够激活一些由Spring支持的高级特性,这通常由特定的实现供应商指定:

执行特定的事务语义(例如用户自定义的事务隔离级别和事务超时)

获取具备事务功能的JDBC Connection (暴露给基于JDBC的DAO)

应用查询超时功能(自动地从Spring管理的事务超时中计算)

及时刷出 PersistenceManager (使得事务变化对于基于JDBC的数据访问代码可见)

从 JDOExceptions 到Spring的 DataAccessExceptions 的高级转化

这对于JDO 1.0的实现有特别的价值,由于这些特性都没有在其标准的API中包含。 对于JDO 2.0,其中的绝大多数的特性已经以标准的方式被支持。因而,Spring的 DefaultJdoDialect 在默认情况下(Spring 1.2后)使用相应的JDO 2.0 API函数。对于特殊的事务语义和异常的高级转化这样的高级特性, 获取和使用JDO实现供应商特定的 JdoDialect 子类还是比较有价值的。

更多有关它的操作以及它如何在Spring的JDO支持中使用的详细信息请参看 JdoDialect 的Javadoc。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值