JSP虚拟主机,jsp空间,java空间,java虚拟空间JSP虚拟主机,jsp空间,java空间,java虚拟空间

在Spring中使用JDO



作者:    文章来源:
发布日期:2007年03月27日
在此前的Spring: A Developer's Notebook摘录中,作者Bruce Tate和Justin Gehtland讨论了在Spring中使用iBATIS OR持久化框架的内容。出于其他的原因,此选择要求深入SQL底层。在这篇第五章的摘录中,他们转向那些更加彻底地把SQL从你的Spring应用中独立的持久化框架。

JDO是基于接口标准的持久化,或如Tate 和Gehtland所言,“在Java应用中非EJB标准的持久化”。一经提及JDO,他们不是卷入那场“众说纷纭”的辩论之中,而是着眼于如何把一顶级的实现,Kodo,引入到你的Spring应用中。接着转去讨论最流行的,且可能是与Spring集成最好的OR框架:Hibernate。正如他们所指出的,“尽管是非标准的,但你可以说在EJB之后,Hibernate是世界上最流行的持久化框架了。”本章中,作者们不仅为你演示设置每个框架的具体细节,而且还清晰地表明了各种框架的使用方法之差异。

就象Developer's Notebook 系列中所有的书一样,你将由直接的且实用的方式获得信息,这些信息包含了完成你工作所需的细节。换言之:“一切来自实战,没有讲义。”

版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:czyczy(作者的blog:http://blog.matrix.org.cn/page/czyczy)
原文:http://www.matrix.org.cn/resource/article/44/44313_Spring+JDO.html
关键字:Spring;JDO

在Spring中使用JDO

JDO是Java 应用中非EJB标准的一种持久化方式。在这部分中,我们将使用我们喜欢的JDO实现,Kodo,来实现我们应用中的透明持久化。虽然我们不会试着教给你JDO的知识,但会说明怎样使用JDO来为我们的应用提供持久化的功能。

无论你从事JDO多长时间,可能都会使你想起众说纷纭的辩论。直到最近,大多数人都避免使用JDO。随着JDO2.0 版本和许多可靠的商业化及开源的JDO实现的暂停发布,这个持久化标准看起来就象是一个强壮的运动员在ORM舞台上蓄势待发(译注:在翻译本文时获悉,JDO2.0已投票通过)。实际上,我所钟爱的ORM是Solar Metric的Kodo, 它或许称得上JDO实现中的矫矫者了。当Kodo达到商业化实现时,它看起来比其它可供选择的实现更加强壮,且已获得更灵活的映射支持、更易于管理,来自持久化企业核心越来越丰富的支持,考虑以下这些优势:
        若你正寻找某些免费的或能对源代码更好地控制的JDO实现,你可在众多的开源的JDO实现中选择。
        你可选择一价格合理的商业化产品,而且能得到支持和维护。
        若选择了那些一流的商业化厂商,从较佳管理到灵活映射,你都可能得到难以置信的功能和性能。
        在获得所有的这些优势的同时,你还能获得开源标准提供的保护和安全。

我该怎么办?

你将使用JDO来创建一个持久化模型,随后在façade层中使用那个模型。尽管应用程序中已创建了业务域模型,但仍未被持久化。你业已获得façade层的接口,那么只需完成下述的操作步骤,便可在你的应用中使用JDO了:
1.通过字节码增强器(byte code enhancer)让域模型持久化。
2.简单修改Spring的配置文件,你就可在你的应用中使用Kodo。
3.通过JDO模板,建构使用持久化模型的façade。

就这些了。Spring会管理由PersistenceManagerFactory和PersistenceManager组成的JDO之核心资源。把这些特殊的选项当作是JDO的数据源与连接。你还可以让Spring来管理事务。上述三个步骤就是你所要完成的工作。

首先,你需要下载和安装Kodo。从试用的版本开始,你可在这里找到http://www.solarmetric.com。本书中使用3.2.1版本。再次提醒,你得把/kodo-jdo-3.2.1/lib中的包放入我们的/lib目录下。

为持久化模型,你得修改Ant任务以添加JDO字节码增强的步骤:象示例5-8所示的那样,添加Ant任务来完成这一动作。

示例5-8.
<taskdef name=”jdoc” classname=”kodo.ant.JDOEnhancerTask” />

<target name=”jdo.enhance”>
        <jdoc>
                <fileset dir=”${src.dir}”>
                        <include name=”**/*.jdo” />
                </fileset>
        </jdoc>
</target>


你也需要在Ant 建构文件中给kodo-jdo.jar, jdo-1.0.1.jar, and Jakarta-commons-lang-1.0.1.jar增加路径元素。

下一步,建构持久化映射。最简便的办法就是通过Kodo向导。运行Workbench(在你所安装的Kodo的in目录下),并从菜单中选择MetaDataàCreate MetaDate。反之,你可使用/kodo-jdo-3.2.1/bin目录下的metadatatool 和mappingtool两个脚本,它们分别被kodo.jdbc.meta.MappingTo-ol和kodo.meta.JDOMetaDataTool的运行文件所使用。

为了与其它JDO版本保持一致,因此你打算用XML从头建构映射文件。用类的元数据和.mapping 文件生成.jdo文件。此两文件都在/war/WEB-INF/classes/com/springbook目录下。

示例5-9声明元数据文件。

示例5-9  package.jdo
<?xml version="1.0" encoding="UTF-8"?>
<jdo>
     <package name="com.springbook">
         <class name="Bike">
               <extension vendor-name="kodo" key="detachable" value="true"/>
               <field name="reservations">
                     <collection element-type="Reservation"/>
                     <extension vendor-name="kodo" key="inverse-owner" value="bike"/>
                     <extension vendor-name="kodo" key="element-dependent" value="true"/>
               </field>
         </class>
        <class name="Customer">
              <extension vendor-name="kodo" key="detachable" value="true"/>
              <field name="reservations">
                  <collection element-type="com.springbook.Reservation"/>
                  <extension vendor-name="kodo" key="inverse-owner" value="customer"/>
                 <extension vendor-name="kodo" key="element-dependent" value="true"/>
              </field>
        </class>
        <class name="Reservation">
             <extension vendor-name="kodo" key="detachable" value="true"/>
        </class>
    </package>
</jdo>


示例5-10描述了映射文件。

示例5-10. package.mapping
<?xml version="1.0" encoding="UTF-8"?>
<mapping>
       <package name="com.springbook">
              <class name="Bike">
                   <jdbc-class-map type="base" pk-column="BIKEID" table="BIKES"/>
                   <field name="bikeId">
                        <jdbc-field-map type="value" column="BIKEID"/>
                    </field>
                    <field name="frame">
                         <jdbc-field-map type="value" column="FRAME"/>
                    </field>
                    <field name="manufacturer">
                         <jdbc-field-map type="value" column="MANUFACTURER"/>
                     </field>
                     <field name="model">
                          <jdbc-field-map type="value" column="MODEL"/>
                     </field>
                     <field name="reservations" default-fetch-group="true">
                          <jdbc-field-map type="one-many" ref-column.BIKEID="BIKEID"
                                 table="RESERVATIONS"/>
                      </field>
                      <field name="serialNo">
                           <jdbc-field-map type="value" column="SERIALNO"/>
                       </field>
                       <field name="status">
                            <jdbc-field-map type="value" column="STATUS"/>
                        </field>
                        <field name="weight">
                             <jdbc-field-map type="value" column="WEIGHT"/>
                        </field>
                   </class>
                   <class name="Customer">
                         <jdbc-class-map type="base" pk-column="CUSTID" table="CUSTOMERS"/>
                         <field name="custId">
                               <jdbc-field-map type="value" column="CUSTID"/>
                         </field>
                         <field name="firstName">
                               <jdbc-field-map type="value" column="FIRSTNAME"/>
                          </field>
                          <field name="lastName">
                               <jdbc-field-map type="value" column="LASTNAME"/>
                          </field>
                          <field name="reservations" default-fetch-group="true">
                               <jdbc-field-map type="one-many" ref-column.CUSTID="CUSTID"
                                       table="RESERVATIONS"/>
                          </field>
                     </class>
                     <class name="Reservation">
                           <jdbc-class-map type="base" pk-column="RESID" table="RESERVATIONS"/>
                           <field name="bike">
                                 <jdbc-field-map type="one-one" column.BIKEID="BIKEID"/>
                           </field>
                           <field name="customer">
                                 <jdbc-field-map type="one-one" column.CUSTID="CUSTID"/>
                           </field>
                           <field name="reservationDate">
                                <jdbc-field-map type="value" column="RESDATE"/>
                           </field>
                           <field name="reservationId">
                                <jdbc-field-map type="value" column="RESID"/>
                           </field>
                      </class>
                </package>
     </mapping>



几乎太容易了。模型本身没有被持久化,那就是我们使用OR技术的原因。尽管你仍需在你的应用程序中有一层代码,也就是我们所说的fa&ccedil;ade层,来调用那个持久化模型。因此你将看到一系列的模板调用。查询器(finder)声明了JDO查询语句,及持久化删除、更新、新增的对象。你已经有了一个接口,但还需要实现fa&ccedil;ade(如示例5-11)。

示例 5-11. KodoRentABike.java

这不是完全的JDO查询语言的查询;它只不过是个过滤器而已。JDO 2.0将会增加一个便利的查询字符串,因而你可以以单一的字符串来添加完全的JDO查询,而毋须建构完全的查询。

public class KodoRentABike extends JdoDaoSupport implements RentABike {
private String storeName;

public List getBikes( ) {
return (List)getJdoTemplate( ).find(Bike.class);
}

public Bike getBike(String serialNo) {
Collection c = getJdoTemplate( ).find(Bike.class, "serialNo == '" + serialNo + "'");
Bike b = null;
if(c.size( ) > 0) {
b = (Bike)c.iterator().next( );
}
return b;
}

public Bike getBike(int bikeId) {
return (Bike)getJdoTemplate( ).getObjectById(Bike.class, new Long(bikeId));
}

public void saveBike(Bike bike) {
getJdoTemplate( ).makePersistent(bike);
}

public void deleteBike(Bike bike) {
getJdoTemplate( ).deletePersistent(bike);
}

//etc.


最后,你需要设定一些配置来把这些都组装在一起。示例5-12先说明了JDO的配置。
示例 5-12. package.properties

# To evaluate or purchase a license key, visit http://www.solarmetric.com
kodo.LicenseKey: YOUR_LICENSE_KEY_HERE
javax.jdo.PersistenceManagerFactoryClass: kodo.jdbc.runtime.
JDBCPersistenceManagerFactory
javax.jdo.option.ConnectionDriverName: com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName: bikestore
javax.jdo.option.ConnectionPassword:
javax.jdo.option.ConnectionURL: jdbc:mysql://localhost/bikestore
javax.jdo.option.Optimistic: true
javax.jdo.option.RetainValues: true
javax.jdo.option.NontransactionalRead: true
javax.jdo.option.RestoreValues: true
kodo.Log: DefaultLevel=WARN, Runtime=INFO, Tool=INFO
kodo.PersistenceManagerImpl: DetachOnClose=true


注意DetachOnClose 选项。它确保了JDO在关闭连接时的延迟加载,因而你应用程序的其它部分 ,象视图(view),只能访问已加载的Bean。

Spring上下文需把JDO持久化管理器,持久化管理工厂,fa&ccedil;ade,以及任何在fa&ccedil;ade上的服务组合在一起。这些都在context中完成(示例5-13)。

Example 5-13. RentABikeApp-servlet.xml
<bean id="jdofactory" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
<property name="configLocation">
<value>E:RentABikeAppwarWEB-INFkodo.properties</value>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jdo.JdoTransactionManager">
<property name="persistenceManagerFactory">
<ref local="jdofactory"/>
</property>
</bean>
<bean id="rentaBike" class="com.springbook.KodoRentABike">
<property name="storeName"><value>Bruce's Bikes</value></property>
<property name="persistenceManagerFactory">
<ref local="jdofactory"/>
</property>
</bean>



记得你已经有了一个使用fa&ccedil;ade的测试用例,因此你可以建构并让它运行起来。

发生了什么事?

这是个展示Spring功能的非常好的例子。尽管你已从根本上颠覆了持久化层的实现,但这并没有影响到应用程序的其它部分。以下就是它怎样工作的说明。

Spring首先使用依赖注入来解决所有的依赖。加载的上下文配置了含有你所提供数据源的JDO,随之在fa&ccedil;ade的JDO实现中设定持久化管理器工厂。接着,当你在fa&ccedil;ade上调用方法时,Spring给了你持久化管理器并使用它来处理你所提供的查询。你可以这样来看:Spring提供一普通的(generic)JDO
fa&ccedil;ade方法,称之为模板。你插入(plug in)细节,并把控制权交给Spring。

当你在骑自行车或编写代码时,一个最重要的衡量标准是效率。每当脚踏板转一圈,或每写一行代码,你可完成多少的工作?想想JDO版本的应用程序,Spring编程模型最迫切的事是效率。为了看明白我说的意思,想一下你没见到的:
&#61623;        没有异常管理,而&#61623;        往往就是这些异常管理混乱了你应用程序的较底层。利用Spring未捕捉异常(Unchecked Exception)的机制,&#61623;        你可以在得到异常的地方适当地做些事情。
&#61623;        没有资源管理。只要有JDBC连接的地方,&#61623;        就有JDO持久化的管理器。Spring配置了持久化管理器工厂,&#61623;        并且在模板内为你管理持久化管理器。
&#61623;        你没有被强制管理事务及facade的安全。Spring让你很容易地来配置这些东西,&#61623;        因此你可以从你的fa&ccedil;ade层中将丑陋的细节束之高阁,&#61623;        让它专注于使用持久化模型。

在Spring的模板中这些都为你完成了,这些模板放在与Spring框架一起的代码中,这些让你读起来更加容易理解,或在必要时debug之。简而言之,你可以在每一行的代码中得到平衡,更象你在高速地踏自行车。那就是所有最成功的框架和程序设计语言的底线。

在Spring中使用Hibernate

Spring开发人员选择Hibernate作为持久化框架已经有很长的一段时间了。尽管Spring团队在持续致力于改善与其它持久化框架的集成,但在所有与Spring集成的持久化框架中, Hibermate保持了最为广泛使用的地位。确切地说,这两个轻量级的解决方案在相互促进中获得成功,他们一起茁壮成长。在这此例中,我们将表明如何将Spring与Hibernate集成起来一同工作。

Hibernate是个优秀的持久化框架。她深受欢迎,运行速度相当快,并且是免费的。除此之外,它还拥有丰富的映射支持,且是一个便利的使用模型,使得它在全球的开发者中流行起来。Hibernate已经在小型和中型的项目中经受住了非常好的考验。实际上,尽管它还未被确认为标准,但可以说在EJB之后,Hibernate是世界上最流行的持久化框架。

我该怎么办?

既然你已用持久化域模型和fa&ccedil;ade 的ORM配置了Spring,你就了解了基本的流程。由于Spring 被设计成与Hibernate 独立,你只需从Spring的/dist 目录中复制hibernate2.jar, aopalliance.jar, cglig-full-2.0.2.jar, dom4j.jar, ehcache-1.1.jar, 及admg.jar到你项目的/lib目录下。

由于Hibernate使用了反射机制, 因此就没有字节加强步骤。所有你已尝试过让模型持久化的做法,就是来创建映射,并且在上下文中引用他们。示例5-14,5-15及5-16表明了创建映射的过程。

Example 5-14. Bike.hbm.xml<hibernate-mapping>
<class name="com.springbook.Bike" table="bikes">
<id name="bikeId" column="bikeid" type="java.lang.Integer"
unsaved-value="-1">
<generator class="native"></generator>
</id>
<property name="manufacturer" column="manufacturer" type="string"/>
<property name="model" column="model" type="string"/>
<property name="frame" column="frame" type="int"/>
<property name="serialNo" column="serialno" type="string"/>
<property name="weight" column="weight" type="java.lang.Double"/>
<property name="status" column="status" type="string"/>
<set name="reservations">
<key column="bikeId"/>
<one-to-many class="com.springbook.Reservation"/>
</set>
</class>
</hibernate-mapping>


Hibernate版的这个映射较之JDO版的相应代码更为雅致。它有着更加丰富的识别码(Identification)生成库。

Example 5-15. Customer.hbm.xml
<hibernate-mapping>
<class name="com.springbook.Customer" table="customers">
<id name="custId" column="custid" type="java.lang.Integer"
unsaved-value="-1">
<generator class="native"></generator>
</id>
<property name="firstName" column="firstname" type="string"/>
<property name="lastName" column="lastname" type="string"/>
<set name="reservations">
<key column="custId"/>
<one-to-many class="com.springbook.Reservation"/>
</set>
</class>
</hibernate-mapping>


Example 5-16. Reservation.hbm.xml

<hibernate-mapping>
<class name="com.springbook.Reservation" table="reservations">
<id name="reservationId" column="resid" type="java.lang.Integer"
unsaved-value="-1">
<generator class="native"></generator>
</id>
<property name="reservationDate" column="resdate" type="date"/>
<many-to-one name="bike" column="bikeId" class="com.springbook.Bike"
cascade="none"/>
<many-to-one name="customer" column="custId" class="com.springbook.Customer"
cascade="none"/>
</class>
</hibernate-mapping>


在上下文中,你需要配置Hibernate属性,配置session factory, 并把session factory插入到fa&ccedil;ade中。使用JDO 上下文、iBATIS上下文及Hibernate上下文的事务策略都是一样的,就想他们本该那样似的。那就是依赖注入正为你所做的一部分。示例5-17表明了对上下文的变动。

Example 5-17. RentABikeApp-servlet.xml
<bean name="rentaBike" class="com.springbook.HibRentABike">
<property name="storeName"><value>Bruce's Bikes</value></property>
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="mappingResources">
<list>
<value>com/springbook/Bike.hbm.xml</value>
<value>com/springbook/Customer.hbm.xml</value>
<value>com/springbook/Reservation.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>


不象JDO那样,Hibernate用户经常把所有的映射类放入独立定的多个文件中。这不是必需的,仅是Hibernate用户特别喜欢管理事前的一种方式而已。

既然已有了一个fa&ccedil;ade接口,你只需一个Hibernate实现即可。你可以使用模板,就象JDO那样。至于查询器(Finder),你所要做的就是指定一个Hibernate 查询语言(HQL)语句。至于更新(update)部分,你所要做的就是指明将被存储的新对象。示例5-18就是个非常薄的fa&ccedil;ade的例子。

Example 5-18. HibRentABike.java
package com.springbook;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import java.util.List;
import java.util.Date;
import java.util.Set;
import net.sf.hibernate.Query;

public class HibRentABike extends HibernateDaoSupport implements RentABike {
private String name;

public List getBikes( ) {
return getHibernateTemplate( ).find("from Bike");
}

public Bike getBike(String serialNo) {
Bike b = null;
List bikes = getHibernateTemplate( ).
find("from Bike where serialNo = ?", serialNo);
if(bikes.size( ) > 0) {
b = (Bike)bikes.get(0);
}
return b;
}

public Bike getBike(int bikeId) {
return (Bike)getHibernateTemplate( ).
load(Bike.class, new Integer(bikeId));
}

public void saveBike(Bike bike) {
getHibernateTemplate( ).saveOrUpdate(bike);
}

public void deleteBike(Bike bike) {
getHibernateTemplate( ).delete(bike);
}

public void setStoreName(String name) {
this.name = name;
}

public String getStoreName( ) {
return this.name;
}

public List getCustomers( ) {
return getHibernateTemplate( ).find("from Customer");
}

public Customer getCustomer(int custId) {
return (Customer)getHibernateTemplate( ).
load(Customer.class, new Integer(custId));
}

public List getReservations( ) {
return getHibernateTemplate( ).find("from Reservation");
}

public List getReservations(Customer customer) {
return getHibernateTemplate( ).
find("from Reservation where custId = ?", customer.getCustId( ));
}


public List getReservations(Bike bike) {
return getHibernateTemplate( ).
find("from Reservation where bikeId = ?", bike.getBikeId( ));
}

public List getReservations(Date date) {
return getHibernateTemplate( ).
find("from Reservation where resdate = ?", date);
}

public Reservation getReservation(int resId) {
return (Reservation)getHibernateTemplate( ).
load(Reservation.class, new Integer(resId));
}
}


发生了什么事?

Hiberante的做法与JDO极其相似。Spring的JDO模板代表着一套默认的DAO方法。你所要做的就是来定制他们,通常是HQL查询语句,及任何HQL参数化查询中参数的可能取值。接着Spring拿到控制权,获得session,发动查询并且处理任何的异常。

再次重申,你看到独特的平衡。示例5-19给了在不使用Spring时,你不得不编写的典型的Hibernate之方法。

Example 5-19. HibRentABike.java
public List getBikesOldWay( ) throws Exception {
// Relies on other static code for configuration
// and generation of SessionFactory. Might look like:
// Configuration config = new Configuration( );
// config.addClass(Bike.class).addClass(Customer.class).
// addClass(Reservation.class);
// SessionFactory mySessionFactory = Configuration.
// buildSessionFactory( );

List bikes = null;
Session s = null;
try {
s = mySessionFactory.openSession( );
bikes = s.find("from Bike");
}catch (Exception ex) {
//handle exception gracefully
}finally {
s.close( );
}
return bikes;
}


示例5-20再次表明了在Spring中实现查找的相应代码。

Example 5-20. HibRentABike.java
public List getBikes( ) {
return getHibernateTemplate( ).find("from Bike");
}


新手需要特别注意本文代码中的黑体字部分,应用程序的每一代码块必须尽力完成好一个功能,且只能一个。

关于…
…Hibernate的替代品?Hibernate确实是免费的、运行速度快的、有效且是流行的。它已经受住了考验,且有着极其优秀的性能和灵活性。大部分的工具中有着很好的Hibernate支持,并且Hibernate支持所有相关的数据库。但我仍只推荐在小型和大型的应用中使用Hibernate。

到目前为止,H作为一开源框架,持久化社区不能过多地赞扬Hibernate或声讨其替代者。如此盲目的拥趸导致了礼教崇拜式的决策。Hibernate的竞争者们在某些方面比她做得更好。若你的应用程序有以下的特征,不妨试一试其他的解决方案:

标准
JDO和JDBC解决方案已然成为标准。尽管Hibernate是开源的,但她还未被确认为标准。在过去的十年中,你得信赖JBoss 组织和基金会的动机来做正确的事情,且对任何的框架而言,至今已证明那是不可靠的地位。

管理
其他的解决方案易于管理,如Kodo JDO和Top Link就有管理面板,用他们很容易地来监控缓存状态,及主动(eager)加载或延迟加载的状态。

映射
其他的框架有着功能更强大的和更加灵活的映射支持。若你不想对你的数据库schema进行控制,你最好选用其他的解决方案。你可能也喜欢用GUI工具来映射你的schema ,那么,象JDO Genie 或Cayenne可能就最适合你的应用。

总而言之,使用主流的框架或许最终可能是正确的选择,但通常情况下,只要稍微深入的探究,你就可以找到更加合适的解决方案。Hibernate确实值得考虑,但别忘了还有其他好的选择。

运行一个测试用例

运行测试用例是很容易的事,你已经学会了。它就是你在fa&ccedil;ade 上运行的那个测试用例。

我该怎么办?

由于测试用例已存在,因此你可以运行现有的fa&ccedil;ade测试。你仅要确保正确设置测试数据即可。且你可以使用未被变更过的应用上下文。那就是Spring的强大之处,可测试性。

发生了什么事?

你使用过已存在的测试用例,这很好,因为你只需要管理数据库增长的细节,在下章中,你将开始深入学习可在Spring AOP的应用程序中添加的服务的相关知识。
Copyright © 2002-2012 JSPCN.net. All rights reserved.
JSP中文网    备案号:粤ICP备09171188号
成都恒海科技发展有限公司    成都市一环路南二段6号新瑞楼三楼8号