前言
在此前的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 系列中所有的书一样,你将由直接的且实用的方式获得信息,这些信息包含了完成你工作所需的细节。换言之:“一切来自实战,没有讲义。”
在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.
你也需要在ant 建构文件中给kodo-jdo.jar, jdo-1.0.1.jar, and jakarta-commons-lang-1.0.1.jar增加路径元素。
下一步,建构持久化映射。最简便的办法就是通过kodo向导。运行workbench(在你所安装的kodo的/bin目录下),并从菜单中选择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
示例5-10描述了映射文件。
示例5-10. package.mapping
几乎太容易了。模型本身没有被持久化,那就是我们使用or技术的原因。尽管你仍需在你的应用程序中有一层代码,也就是我们所说的fa?ade层,来调用那个持久化模型。因此你将看到一系列的模板调用。查询器(finder)声明了jdo查询语句,及持久化删除、更新、新增的对象。你已经有了一个接口,但还需要实现fa?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.comkodo.licensekey: your_license_key_herejavax.jdo.persistencemanagerfactoryclass: kodo.jdbc.runtime.jdbcpersistencemanagerfactoryjavax.jdo.option.connectiondrivername: com.mysql.jdbc.driverjavax.jdo.option.connectionusername: bikestorejavax.jdo.option.connectionpassword:javax.jdo.option.connectionurl: jdbc:mysql://localhost/bikestorejavax.jdo.option.optimistic: truejavax.jdo.option.retainvalues: truejavax.jdo.option.nontransactionalread: truejavax.jdo.option.restorevalues: truekodo.log: defaultlevel=warn, runtime=info, tool=infokodo.persistencemanagerimpl: detachonclose=true注意detachonclose 选项。它确保了jdo在关闭连接时的延迟加载,因而你应用程序的其它部分 ,象视图(view),只能访问已加载的bean.spring上下文需把jdo持久化管理器,持久化管理工厂,fa?ade,以及任何在fa?ade上的服务组合在一起。这些都在context中完成(示例5-13)。example 5-13. rentabikeapp-servlet.xml e:/rentabikeapp/war/web-inf/kodo.propertiesbruce's bikes
记得你已经有了一个使用fa?ade的测试用例,因此你可以建构并让它运行起来。
发生了什么事?
这是个展示spring功能的非常好的例子。尽管你已从根本上颠覆了持久化层的实现,但这并没有影响到应用程序的其它部分。以下就是它怎样工作的说明。
spring首先使用依赖注入来解决所有的依赖。加载的上下文配置了含有你所提供数据源的jdo,随之在fa?ade的jdo实现中设定持久化管理器工厂。接着,当你在fa?ade上调用方法时,spring给了你持久化管理器并使用它来处理你所提供的查询。你可以这样来看:spring提供一普通的(generic)jdo fa?ade方法,称之为模板。你插入(plug in)细节,并把控制权交给spring.
当你在骑自行车或编写代码时,一个最重要的衡量标准是效率。每当脚踏板转一圈,或每写一行代码,你可完成多少的工作?想想jdo版本的应用程序,spring编程模型最迫切的事是效率。为了看明白我说的意思,想一下你没见到的:? 没有异常管理,而? 往往就是这些异常管理混乱了你应用程序的较底层。利用spring未捕捉异常(unchecked exception)的机制,? 你可以在得到异常的地方适当地做些事情。? 没有资源管理。只要有jdbc连接的地方,? 就有jdo持久化的管理器。spring配置了持久化管理器工厂,? 并且在模板内为你管理持久化管理器。? 你没有被强制管理事务及facade的安全。spring让你很容易地来配置这些东西,? 因此你可以从你的fa?ade层中将丑陋的细节束之高阁,? 让它专注于使用持久化模型。
在spring的模板中这些都为你完成了,这些模板放在与spring框架一起的代码中,这些让你读起来更加容易理解,或在必要时debug之。简而言之,你可以在每一行的代码中得到平衡,更象你在高速地踏自行车。那就是所有最成功的框架和程序设计语言的底线。
在spring中使用hibernate
spring开发人员选择hibernate作为持久化框架已经有很长的一段时间了。尽管spring团队在持续致力于改善与其它持久化框架的集成,但在所有与spring集成的持久化框架中, hibermate保持了最为广泛使用的地位。确切地说,这两个轻量级的解决方案在相互促进中获得成功,他们一起茁壮成长。在这此例中,我们将表明如何将spring与hibernate集成起来一同工作。
hibernate是个优秀的持久化框架。她深受欢迎,运行速度相当快,并且是免费的。除此之外,它还拥有丰富的映射支持,且是一个便利的使用模型,使得它在全球的开发者中流行起来。hibernate已经在小型和中型的项目中经受住了非常好的考验。实际上,尽管它还未被确认为标准,但可以说在ejb之后,hibernate是世界上最流行的持久化框架。
我该怎么办?
既然你已用持久化域模型和fa?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
unsaved-value="-1">
hibernate版的这个映射较之jdo版的相应代码更为雅致。它有着更加丰富的识别码(identification)生成库。
example 5-15. customer.hbm.xml
unsaved-value="-1"> example 5-16. reservation.hbm.xml unsaved-value="-1">cascade="none"/>cascade="none"/>
在上下文中,你需要配置hibernate属性,配置session factory, 并把session factory插入到fa?ade中。使用jdo 上下文、ibatis上下文及hibernate上下文的事务策略都是一样的,就想他们本该那样似的。那就是依赖注入正为你所做的一部分。示例5-17表明了对上下文的变动。
example 5-17. rentabikeapp-servlet.xml
bruce's bikesclass="org.springframework.orm.hibernate.localsessionfactorybean">
com/springbook/bike.hbm.xmlcom/springbook/customer.hbm.xmlcom/
springbook/reservation.hbm.xmlnet.sf.hibernate.dialect.mysqldialecttrue
不象jdo那样,hibernate用户经常把所有的映射类放入独立定的多个文件中。这不是必需的,仅是hibernate用户特别喜欢管理事前的一种方式而已。
既然已有了一个fa?ade接口,你只需一个hibernate实现即可。你可以使用模板,就象jdo那样。至于查询器(finder),你所要做的就是指定一个hibernate 查询语言(hql)语句。至于更新(update)部分,你所要做的就是指明将被存储的新对象。示例5-18就是个非常薄的fa?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?ade 上运行的那个测试用例。
我该怎么办?
由于测试用例已存在,因此你可以运行现有的fa?ade测试。你仅要确保正确设置测试数据即可。且你可以使用未被变更过的应用上下文。那就是spring的强大之处,可测试性。
发生了什么事?
你使用过已存在的测试用例,这很好,因为你只需要管理数据库增长的细节,在下章中,你将开始深入学习可在spring aop的应用程序中添加的服务的相关知识。
作者:czyczy(作者的blog:http://blog.matrix.org.cn/page/czyczy)
闽公网安备 35060202000074号