JAVA涂鸦
关于JAVA的点点滴滴
posts - 50,  comments - 689,  trackbacks - 0

这一章包括:

n      使用 DAO 模式创建抽象层。

n      使用层的父型模式简化 resource cleanup (资源清除)代码

n      spring 组织你的项目

 

理解 Hibernate 的底层将需要很长一段时间才能在你的项目上使用。但是在 Hibernate 库的基础之外,如 SessionFactory (会话工厂), Session (会话), mapping files ( 映射文件) hibernate Query Language(HQL) Hibernate 查询语言),在一定高度组织一个应用程序并不是总是那么清楚的。 你能在你的项目中应用一些模式和好的经验。一些好的经验来自于社区;而另外一些来自应用于持久化的Java企业模式。这一章的目的就是带给你这些。

    写程序有点像用小朋友的 alphabet blocks (译者注:一种表面有字母的积木)建塔。如果你是建个小塔,那么你不需要仔细关心是怎么堆的。但如果你建一个大的塔,那么你可能要站得更高,你需要稍微不同的技术设置。多一点计划,多一点怎样堆的技术和可能使用一些超强的粘合剂。

    无论是建立玩具塔还是写软件都要使用好的工具和技术。因此这一章就是怎样建立一座高塔。我们将讨论一些普通的模式: Data Access Object(DAO) 封装底层数据操作 )和层的父型模式。另外, spring , 流行的开源项目将为简化你的代码提供组织的工具。总之,这一章将给这个工具一个总的看法和怎样简化你的 Hibernate 项目。

 

章节目标:

在这一章,你将完成一些几点:

n         创建抽象层,使用 DAO 模式集中 SQL 语句,因此简化客户端对象使用。

n         使用层的父型模式改善 DAO 对象,简化 resource cleanup (资源清除)代码。

n         使用 spring 组织和简化你的 DAO 代码。

 

前提条件:

假设你已经完成以下几点:

n         熟悉模式的概念。

n         理解 session (会话)和 transaction (事务)的工作原理,特别是怎样使用持久化被打开 session (会话)链接的对象。

n         你正在寻找一种能组织大型项目的技术。也就是说,使用好的框架代码将利于项目的以后的扩展。

 

7.1 无处不在的 DAO 模式

大多数 Java/J2EE 开发者都或多或少熟悉 DAO 模式。它是 Sun 公司特地在 Java 蓝图中强调的核心模式之一,在许多 Java 书籍中都提到过。它是在使用持久化数据存储的应用程序中的采取的第一个首要模式。一般使用在使用 SQL 的应用程序中,同样也适用于 Hibernate 的应用程序。

 

7.1.1 集中 HQL

DAO 模式的目的能在在一个简单问题的答案中找到:你的数据操作代码放在哪里?如果你面对一个遗留程序而苦恼,因为这个程序中 SQL 代码就像鸟枪发射那样四处都是,而你又没有这方面的经验。其实这并没什么。需要从新命名表中的列吗?准备好手中的枪,在整个程序中修改,以确保你没有遗漏任何 SQL 语句。

DAO 模式鼓励开发者集中 SQL 语句。那么怎样才是好的 SQL HQL 呢?在应用程序中将 HQL 放在一个地方以便以后更容易维护和修改。新手可能不能很好的确定将 HQL 放在哪?他们将 HQL 放在DAO中。 Figure 7.1 显示了 Event EventDao 怎样和数据库互动。

springdao1.jpg

Figure 7.1 Event EventDao 与数据库互动的图

 

通过前面章节知道,一个健壮的对象查询语句需要有错误处理管理,事务和 resource cleanup (资源清除)。在余下的程序中最好隐藏以上那些。例如接下来的 Hibernate 程序,使程序更容易改变 ORM object/relational mapping 对象关系映射 )实现(例如:改变成 JDO 1。更重要的是,这个策略简化了客户端怎样和持久化层互动;他们不需要知道 session (会话), transaction (事务)的边界,或者在他们使用之后是否被清除。

 

DAO 也有类型

    你能使用 DAO 两种基本类型中的一种:

n         应用级 DAO(DAO per application) :在一个应用中有一个中心的 DAO 对所有的实体对象添、删、改、查。

n         类级 DAO(DAO per class) :每个实体类都有自己的 DAO ,用于自身实例的添、删、改、查。 Event 对象由 EventDao 负责。

你也可以应用其他镜像变量,例如使用模块级 DAO(DAO per module) ,但是最终的选择还是依赖于你有少个持久化对象。在有许多类的情况下,我们偏爱使用类级DAO策略。应用级 DAO 策略会出现“ bloatware ”类。第二,与类级 DAO 命名对称能更好的记忆。如果你需要找到 Event 类,你就能够记得起它的 DAO EventDao 。最后,它满足开-关原则,原则规定类将因为扩充而打开,因为修改而关闭。你能够添加一个新的持久化类,而不需要修改中心的 DAO 。因此,让我们在示例中使用类级 DAO

 

简单的 DAO

    Listing 7.1 显示了一个简单的 DAO ,它能处理满足大多数实体需要的基本的CRUD(添、删、改、查)业务。除了这些功能,它还有一些功能,因此客户端对象不用担心。

n         包括业务级 session session per operation ;每个 session (会话)都有添、删、改、查方法。

n         提供一个业务级 transaction (事务) (transaction per operation) 。客户端对象不用担心开始和提交事务。

n         Hibernate2.1 中,处理捕获和 Hibernate 抛出的预期异常,将它们变为非预期异常,将不会使客户端代码混乱。如果你使用 Hibernate3.x ,你能够让没有从新抛出的非预期异常通过。( In Hibernate 2.x, handles catching and handling the checked exceptions that Hibernate throws, turning them into unchecked exceptions, which won’t clutter up client code. If you use Hibernate 3.x, you can just let the unchecked HibernateException s go without rethrowing .

n         输入特性鲜明和明确的 DAO EventDat 仅为 Event 工作,意思就是客户端代码不必执行手动的操作。( Features strongly typed and explicit DAOs; the EventDao only works with Event s, meaning client code doesn’t have to perform manual casting.

 

Listing 7.1 一个简单的有 添、删、改、查方法的 EventDao

---------------------------------------------------------------------------------------------

package  com.manning.hq.ch07;
 
import  org.hibernate.HibernateException;
import  org.hibernate.Session;
import  org.hibernate.Transaction;
import  org.apache.commons.logging.Log;
import  org.apache.commons.logging.LogFactory;
import  com.manning.hq.ch07.Event;
import  com.manning.hq.ch07.HibernateFactory;
import  java.util.List;

/**
* DAO管理持久Evnet
*/
public   class  SimpleEventDao {

    Log log 
=  LogFactory.getLog(SimpleEventDao. class );
    private  Session session;
    private  Transaction tx;

    public  SimpleEventDao() {
        HibernateFactory.buildIfNeeded();
// 初始化SessionFactory。
    }
 
    public   void  create(Event event)  throws  DataAccessLayerException {
        try  {
            startOperation();
// 打开Session和开始transaction
            session.save(event);
// 保存Event。
            tx.commit();
        } 
catch  (HibernateException e) {
            handleException(e);Rolls back和抛出异常。
        } 
finally  {
            HibernateFactory.close(session);
        }
    }

    public   void  delete(Event event)  throws  DataAccessLayerException {
        try  {
            startOperation();
            session.delete(event);
            tx.commit();
        } 
catch  (HibernateException e) {
            handleException(e);
        } 
finally  {
            HibernateFactory.close(session);
        }
    }

    public  Event find(Long id)  throws  DataAccessLayerException{

        Event event 
=   null ;
        try  {
            startOperation();
            event 
=  (Event) session.load(Event. class , id);
            tx.commit();
        } 
catch  (HibernateException e) {
            handleException(e);
        } 
finally  {
            HibernateFactory.close(session);
        }
        return  event;
    }

    public   void  update(Event event)  throws  DataAccessLayerException {
        try  {
            startOperation();
            session.update(event);
            tx.commit();
        } 
catch  (HibernateException e) {
            handleException(e);
        } 
finally  {
            HibernateFactory.close(session);
        }
    }

    private   void  handleException(HibernateException e)  throws  DataAccessLayerException {
        HibernateFactory.rollback(tx);
        throw   new  DataAccessLayerException(e);
        //  二选一,你能够从新抛出,就像…
        //  throw e;
    }

    private   void  startOperation()  throws  HibernateException {
        session 
=  HibernateFactory.openSession();
        tx 
=  session.beginTransaction();
     }
}


package  com.manning.hq.ch07;

public   class  DataAccessLayerException  extends  RuntimeException {
// 其他的构造函数省略。
    public  DataAccessLayerException(Throwable cause) {
        super (cause);
    }
}

---------------------------------------------------------------------------------------------

    SimpleEventDao 是个极其简单的 DAO ,它有添,删,改,查四种方法。每种方法在一个 transaction (事务),打开和关闭一个 session (会话)范围内处理业务。它是明确的,也就是说每个 Event 都有独有的业务处理,因此客户端类不需要再处理这些了。当这个实现是简单的(以后可能随着扩展会变大),客户端代码在运行 event 时代码就会变得很短。( While this implementation is simple (perhaps overly so, as we will explore here later), it greatly shortens the client code that works with events. )因此创建和查询一个event就象下面的一样简单:---------------------------------------------------------------------------------------------

Event event  =   new  Event();
event.setName(
" A new Event " );
EventDao eventDao 
=   new  EventDao();
eventDao.create(event);
Event foundEvent 
=  eventDao.find(event.getId());

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

正如你看到的,不需要处理凌乱的异常,这里也不需要处理 resource cleanup (资源清除)。现在让我们讨论更多一些这个实现出现的问题,并且我们该怎样提高它。

 

7.2 分析 DAO

    我们在前面章节练习的简单的DAO实现出现了一些问题,有些你或许已经遇到。让我们看一看。

 

7.2.1 boilerplate code 样板代码)

Listing 7.1 包括许多资源管理和异常处理代码。每个方法都有打开 session (会话),开始事务,执行自己的商务业务,提交事务,事务回滚,最后关闭 session (会话)。每个方法基本就像下面那样:

-------------------------------------------------------------------------------

  try  {
     startOperation();
     session.save(event);
     tx.commit();
 catch (HibernateException e) {
     handleException(e);
 finally {
     HibernateFactory.close(session);
 }

-------------------------------------------------------------------------------

Session.save(event) 这一行是每个方法中唯一改变的。甚至 refactoring 几个便利的方法(译者注:找了不少词典,都没有找到“ refactor ”),如 startOperation () handleException () ,都不能完全摆脱你的样板代码。( Even refactoring out a few convenience methods,such as startOperation() and handleException() , doesn’t completely rid you of boilerplate code )一个 potential solution (潜在的解决方案)就是在7.3节讨论的层的父型模式。

 

7.2.2 P otential duplication 潜在的复制)

    增加一个新的 DAO 变得很简单,只需要复制和粘贴。如果你需要其他的 DAO ,如 LocationDao , 我们将复制和粘贴 EventDao ,然后在每个方法中改变关联的少许几行代码。因为我们知道复制是所有程序的恶魔之首,显然需要做点事情。建立层的父型模式是非常有帮助的。

 

7.2.3 游离对象

    因为每个方法都是与单一的 session (会话)和 transaction (事务)运作的,与 DAO 运作的所有的 Event 是严格的游离对象。这个行为也许是美好的,但它不能够利用 Hibernate 的自动脏对象检查和 session-level object cache (会话级对象缓存)。例如,假如客户端这样写:

---------------------------------------------------------------------------------------------

Event foundEvent  =  eventDao.find(event.getId());
foundEvent.setDuration(
30 );
eventDao.update(foundEvent);

---------------------------------------------------------------------------------------------

Find 运行在一个 session (会话)中, update 运行在另一个 session (会话)中。它一定会运行,但如果 find update 能以某种方法共享一个 session (会话)那才是真正的好。并且,它能更好的避免在 session (会话)周围的凌乱的方法署名。当它运作时,它是难看的,因此我们不想看到下面这样:

---------------------------------------------------------------------------------------------


Session session 
=  HibernateFactory.openSession();
Event foundEvent 
=  eventDao.find(event.getId(), session);
foundEvent.setDuration(
30 );
eventDao.update(foundEvent, session);
 

---------------------------------------------------------------------------------------------

    给方法增加 session (会话)参数,强制责任,管理和在客户端代码共享 session (会话)。这样会带来重复,复杂和潜在的错误。

    这个问题的一个潜在的解决方案是众所周知的 Thread Local Session 模式。这个模式将在第八章介绍,因此我们不会在这里直接介绍。我们改用另外一种框架练习, spring ,它使用 Thread Local Session 模式,在下面会有介绍。

 

7.3 层的父型模式

    常规的 J2EE 智者认为应用程序被划分成不同的层。假设你的应用程序已经有这些层了,当然,依赖于你所读的书。一些普遍的层的选择如下:

n         表示层,这里是所有用户交互和表示的代码。

n         领域层,这里是逻辑的商务代码。

n         持久层,这里是数据存储操作的代码。

    不管你的应用程序是否有这些层,在层中的每个对象都有某些共同的代码能够被固定到一个类中,这一点是非常普通的。这个原因出现了层的父型模式,每一层都有“一种类型,这种类型作为所有的层中的类型的父型”2。你能够使用父型模式简化你的 DAO

    Figure7.2 用一个简单的层次显示了层的父型 AbstractDao ,它提供了 protected 方法,在子类中可以覆盖和改为 public

springdao2.JPG

    Figure 7.2 层的父型 AbstractDao 的图

    下一步就是创建 AbstractDao ,下一节将介绍。

 

7.3.1 创建 AbstractDao

    第一步是创建你的父型, AbstractDao ,所有的 DAO 最后将扩展。 Listing 7.2 显示了类的内容。

Listing 7.2 层的父型实现, AbstractDao ,它有所有 DAO 的共同的业务。

-----------------------------------------------------------------------------------------
  

package com.manning.hq.ch07;

import org.hibernate.HibernateException;
import org.hibernate.Query;import org.hibernate.Session;
import org.hibernate.Transaction;
import java.util.List;
  
/**
* 层的父型处理所有DAO共同的运作
*/

public abstract class AbstractDao{

    private Session session;
    private
 Transaction tx;

    public AbstractDao() {
        HibernateFactory.buildIfNeeded();
    }

    protected void saveOrUpdate(Object obj){//运作是普通的,而不是特指域对象
        try {
            startOperation();
            session.saveOrUpdate(obj);
            tx.commit();
        } 
catch (HibernateException e) {
            handleException(e);
        } 
finally {
            HibernateFactory.close(session);
        }
    }

   
protected void delete(Object obj) {
        try {
            startOperation();
            session.delete(obj);
            tx.commit();
        } 
catch (HibernateException e) {
            handleException(e);
        } 
finally {
            HibernateFactory.close(session);
        }
    }

    protected
 Object find(Class clazz, Long id){//查找基于类和id的持久化对象

        Object obj 
= null;

        try {
            startOperation();
            obj 
= session.load(clazz, id);
            tx.commit();
        } 
catch (HibernateException e) {
            handleException(e);
        } 
finally {
            HibernateFactory.close(session);
        }
        return obj;
    }

    protected List findAll(Class clazz) {

        List objects 
= null;

        try {
            startOperation();
            Query query 
= session.createQuery("from " + clazz.getName());
            objects 
= query.list();
            tx.commit();
        } 
catch (HibernateException e) {
            handleException(e);
        } 
finally {
            HibernateFactory.close(session);
        }
        return objects;
    }

    protected void handleException(HibernateException e)  throws DataAccessLayerException {
        HibernateFactory.rollback(tx);
       throw new DataAccessLayerException(e);
    }

    protected void startOperation() throws HibernateException {
        session 
= HibernateFactory.openSession();
        tx 
= session.beginTransaction();
    }
}


--------------------------------------------------------------------------------------------

    在这个Listing中,你看到了共同的CRUD方法,包括save, find和delete方法都放入AbstractDao类中。他们是 generic和protected,因此子类能够调用它们。这样你的EventDao将简化。这里是一个简单的简化了的方法:

---------------------------------------------------------------------------------------------

public     class   ImprovedEventDao   extends   AbstractDao {
//  其他的方法省略
    public   void  create(Event event)  throws  DataAccessLayerException {
        saveOrUpdate(event);
    }

    public  Event find(Long id)  throws  DataAccessLayerException {
        return  (Event) find(Event. class , id);
    }
}
--------------------------------------------------------------------------------------------
    ImprovedEventDao的仅有的责任就是执行业务和委托调用父类。样板代码和潜在的复制的双重问题得到解决。当我们增加一个新的实体对象时,如:Locations或者Speakers,添加新的DAO
--使用AbstractDao作为层的父型,将是非常迅速的。
--------------------------------------------------------------------------------------------

public classImprovedLocationDao extendsAbstractDao {
//其他的方法省略
    publicvoid create(Location location) throws  DataAccessLayerException {
        saveOrUpdate(location);
    }

    publicLocation find(Long id)  throws  DataAccessLayerException {
        return  (Location) find(Location. class , id);
    }
}
---------------------------------------------------------------------------------------------

通过层的父型的介绍,剩余的问题得到解决, DAO 与游离对象运作。我们想在我们的方法中共享会话,甚至通过不同的 DAO 。为了做到这一点,我们将学习一个新的流行框架, Spring .

 

7.4 Spring 框架

       我们已经注意到已经定义的 DAO 实现中的一些缺陷。复制资源管理代码和使用业务级的 session session per operation )使解决方案会比我们需要的更加复杂,而且也没有我们喜欢的灵活。我们一定能过编写更好的更健壮的解决方案。幸运的是,我们不需要担心 --- 一个优秀的开源解决方案, Spring 框架已经提供给我们了。

       Spring 解决了比帮助我们解决 Hibernate 更多的问题。这是事实,“一个轻量级的容器,允许开发者连接商务对象, DAO 和资源就像 JDBC DataSources Hibernate SessionFactories 。” 3   它使用中心 XML 配置文件去管理资源,甚至它自身的 web MVC 框架( Model-View-Controller )。 Spring 是普通的框架,就是说它可以使用在许多不同的位置中。如果你不熟悉 Spring ,你也许会想我该怎样使用它,你也许认为他仅仅是个框架,假设用来提供某种编译。因此我们将在这里展示。

       Spring 已经被考虑成熟的分割为稳固的集中的若干模块,包括我们先前提到的 MVC web 框架, JDBC 支持, aspect-oriented programming AOP )( 面向剖面编程 )和 ORM 模块。这样就允许你使用其中你需要的,而不用学习或者考虑其他的。为了我们的目的,我们将仅仅学习怎样简化你的 Hibernate 代码,也就是 ORM 模块。最佳的开始的地方是模板。

       首先,你需要获得 Spring 框架的副本,你可以在 www.springframework.org 找到。解压到 applications 目录下的 Hibernate 旁边。 Spring 有许多可选择的包,但为了简单,你只需要考虑一个 JAE 包,它就是 applications\spring-framework-1.2-rc2\dist\spring.jar 文件。将它加入到 build.xml 文件的 classpath 中。

-------------------------------------------------------------------------------------------------------------------------

< property name = " spring.version "  value = " 1.2-rc2 " />
< property name = " spring.lib.dir "
    value
= " ${applications.dir}/spring-framework-${spring.version} " />
< path id = " spring.lib.path " >
< fileset dir = " ${spring.lib.dir}\dist " >
< include name = " **/spring.jar " />
</ fileset >
</ path >
< path id = " runtime.classpath " >
//  其他的 paths 省略。
< path refid = " spring.lib.path " />
< path >

-------------------------------------------------------------------------------------------------------------------------

       这个代码配置了 Spring 使它能过在我们的示例项目中使用。 Hibernate3 最近已经发布(在出版的时候),其他支持项目如 Spring 也跟上提供支持了。这里我们使用最新的版本。另外,因为 Spring 不得不支持 Hibernate 2 Hibernate 3, 一个新的包, org.springframework.orm hibernate3 已经将它加入到 Hibernate3 的项目中了。接下来,让我们看看 Spring 怎样被使用来简化我们的示例项目。

 

7.4.1 在模板中是什么

    Spring 给我们提供了 Hibernate 业务的模板。模板有什么,我们为什么需要它?答案就在我们的 SimpleEventDao 中的 create 方法中。

--------------------------------------------------------------------------------------------

protected   void  create(Event event) {
    try  {
        startOperation();
        session.save (event);
        tx.commit();
    } 
catch  (HibernateException e) {
        handleException(e);
    } 
finally  {
        HibernateFactory.close(session);
    }
}

--------------------------------------------------------------------------------------------

    如果你注意了,方法中只调用一个真正的事件是: save ()。其他的每一行,我们喜欢称为“ excise (税)”。 excise (税)是我们为了工作不得不去做的额外的事情,这些事情不是真正的直接重要。它就像当你开车去上班,驾驶的实际行动才是重要的事情;开车库门和倒车都是 excise (税)任务,它可以看成同等重要,也可以忽略或者自动完成。

       在程序中, excise (税)是框架或者语言安全需要,你不得不编写的代码。一个典型的 excise (税)例子就是 Java 除去了内存管理。 Spring 能够除去 Hibernate JDBC 要求之下的资源管理 excise (税)的一部分。

       一般情况下,当你复制代码,你能够 refactor 一个方法或类。这里,因为复制代码是商务方法周围的 resource  cleanup (资源清理),使它更加复杂。现在有模板可用。导入 Spring 提供的类: org.springframework.orm.hibernate3.HibernateTemplate 。它包含了所有资源处理代码以致于你仅只要写一个重要方法就可以了。我们的 create() 方法能够这样写:

----------------------------------------------------------------------------------------------------------------------------------------------------

import  org.hibernate.Hibernate;
import  org.hibernate.SessionFactory;
import  org.springframework.orm.hibernate3.HibernateTemplate;

protected   void  create(Event event) {
    SessionFactory sf 
=  HibernateFactory.getSessionFactory();
    HibernateTemplate template 
=   new  HibernateTemplate(sf);
    template.saveOrUpdate(event);
}

-------------------------------------------------------------------------------------------------------------------------

注意我们没有做什么:

n         SessionFactory 获得 session

n         开始事物

n         捕获预期异常和将他转变为非预期异常( 3.x 版本不需要, 2.x 则需要)

n         提交事物

n         将改变输入数据库

n         关闭 session

这里有许多事情我们不需要担心,因为 HibernateTemplate 已经照顾到了。你或许注意到 HibernateTemplate 看上去像包裹 Session 的周围。实际上,可以把 HibernateTemplate 理解为一个“聪明”的 Session ,它知道怎样打开,关闭和在运行完清除。在之前默认的情况下,它使用的是方法级事务( transaction per method )模式。这是非常简单的,但你将在后面看到你也能够改事务的范围。这里有两个基本的方式和 Hibernatetemplate 互动:通过 convenience mehod (便利的方法)和重要的 callback (回调)。

 

Convenience methods (便利的方法)

    简单的事情将变得容易。在许多案例中,你想用 Session (会话)做什么是非常重要的:执行保存,更新有力对象,或者运行 HQL 查询。没有一个需要礼仪来获得完成。 HibernateTemplate 类提供了基本的方法,因此一行代码就可以简单的调用业务。这里有一些方法的简单样例:

---------------------------------------------------------------------------------------------

import  com.manning.hq.ch07.Event;
import  com.manning.hq.ch07.HibernateFactory;
import  org.springframework.orm.hibernate3.HibernateTemplate;
import  java.util.List;

SessionFactory sessionFactory 
= HibernateFactory.getSessionFactory();
//  创建连接 SessionFactory 的模板
HibernateTemplate template 
= new  HibernateTemplate(sessionFactory);
Event event1 
=   new  Event();
event1.setName(
" Event 1 " );
Event event2 
=   new  Event();
event2.setName(
" Event 2 " );

    try  {
        template.save (event1);   
//  在一个事务中保存 event
        template.save (event2);
//  加载一个 event
        Event obj 
=  (Event) template.load(Event. class , event1.getId());
        System.out.println(
" Loaded the event "   +  obj.getName()); //  找到所有的 event
        List events 
=  (List) template.find( " from Event " );
        System.out.println(
" # of Events  "   +  events.size());
    } 
finally  {
        template.delete(event1); 
//  删除一个 event
        template.delete(event2);
    }

---------------------------------------------------------------------------------------------

convenience methods 便利的方法)是有代表性的正确的命名,就像 Session (会话)中的方法一样。他们能被 session (会话)作为 one-for-one (一对一)复位直接调用,没有任何杂乱的 resource cleanup (资源清除)代码障碍。

 

Callback (回调)

    复杂的事情可能有好处。不是所有的在单个 transaction ( 事务)中的单个 query (查询)能够轻松的减少业务。在这些业务中, spring 提供了 Callback 接口。它允许你在将要执行的模板中编写 callback 方法。例如,如果你想构建一个复杂的 query (查询),更新一些数据,然后保存,所有的都在一个业务中:

-------------------------------------------------------------------------------------------

import  org.hibernate.HibernateException;
import  org.springframework.orm.hibernate3.HibernateCallback;
import  java.sql.SQLException;
import  org.hibernate.Query;
import  java.util.List;
import  java.util.Iterator;
import  com.manning.hq.ch07.Event; 

template.execute(
new  HibernateCallback() {
    public  Object doInHibernate(Session session)  throws  HibernateException, SQLException {
       Query query 
=  session.createQuery( " from Event " );
        query.setMaxResults(
2 );
        List events 
=  query.list();
        for  (Iterator it  =  events.iterator(); it.hasNext();) {
            Event event 
=  (Event) it.next();
            event.setDuration(
60 );
        }
        return   null ;
    }
}) ;

---------------------------------------------------------------------------------------------

这里, Callback 接口使用了匿名内部类, HibernateCallback ,定义了一个单一的方法, doInHibernate() 。你可以编写方法主体,然后提交 HibernateCallback 对象给模板,然后执行。模板处理资源管理代码,让你只需要编写任务的 query (查询)逻辑。

 

7.4.2 Beans 和 它们的 factories (工厂)

你已经看到能够程序化使用 Spring 去减少 resource cleanup (资源清除)代码。另外i,它能过更好的组织项目。 Spring 的惯例的说法是“轻量级”容器。它胜任执行和配置简单的 JavaBeans 。它基本扮演构建和配置你应用程序中的 beans 的工厂角色。意思是它能够被使用去配置大多数现存的 architectures (框架)和库,包括 Hibernate

 

中心的配置文件

    在这一点上,你将通过组合使用 hibernate.cfg.xml 文件( declaratively 声明性的 ))和使用灵活的编程方式,如HibernateFactory来配置HibernateSpring提供了另外一个方法整体声明配置Hibernate。使用Spring的最大好处就是你能够减少编程配置需要的元素。

 

Spring 能够读用一般配置格式编写的 XML 文件。 XML 指定了怎样连接不同对象,包括 DataSource (数据源), SessionFactory 和所有的 DAOs 。一旦你配置了文件,你就能够将它作为查找 DAOs 的中心“票据交换所”使用。例如,在 classpath root (根部)创建一个叫 applicationContext.xml 文件。就像 listing7.3 所显示那样。

 

listing7.3 ApplicationContext.xml ,定义了 DataSource (数据源), SessionFactory DAO

-------------------------------------------------------------------------------

   <? xml version = " 1.0 "  encoding = " UTF-8 " ?>

  <! DOCTYPE beans PUBLIC

      " -//SPRING//DTD BEAN//EN "

      " http://www.springframework.org/dtd/spring-beans.dtd " >

    <
beans >
        < bean id = " dataSource " class = " org.apache.commons.dbcp.BasicDataSource "           destroy - method = " close " >    ①
        < property name = " driverClassName " >
            < value > com.mysql.jdbc.Driver </ value >
        </ property >
        < property name = " url " >
            < value > jdbc:mysql: // localhost/events_calendar</value>
        </ property >
        < property name = " username " >
            < value > root </ value >
        </ property >
        < property name = " password " >
            < value ></ value >
        </ property >
        </ bean >

        < bean id = " factory " class = " org.springframework.orm.hibernate3.LocalSessionFactoryBean " >  ②
        < property name = " mappingResources " >
            < list >
                < value > com / manning / hq / ch07 / Event.hbm.xml </ value >
                < value > com / manning / hq / ch07 / Location.hbm.xml </ value >
            </ list >
        </ property >
        < property name = " hibernateProperties " >
            < props >
                < prop key = " hibernate.dialect " > org.hibernate.dialect.MySQLDialect </ prop >
                < prop key = " hibernate.show_sql " > false </ prop >
            </ props >
        </ property >
        < property name = " dataSource " >    ③
            < ref bean = " dataSource " />
        </ property >
        </ bean >

        < bean id = " eventDao " class = " com.manning.hq.ch07.EventSpringDao " >     ④
        < property name = " sessionFactory " >      ⑤
            < ref bean = " factory "   />
        </ property >
        </ bean >
    </ beans >

-------------------------------------------------------------------------------

listing7.3 解释

    配置一个基本的数据源,它使用由 Hibernate 发布的 Apache Commons database connection pool (DBCP)

       配置一个 SessionFactory ,构建在 Spring SessionFactory wrapper, LocalSessionFactoryBean 。当 Spring 读取这个文件时,它就构建一个 SessionFactory SessionFactory 存储在 Key factory

        连接 SessionFactory 和数据源。

        配置你的 EventSpringDao eventDao

        连接 DAO session factory 。他允许 DAO 打开 session 和发布 queries

-------------------------------------------------------------------------------------------------------------------------

Listing 7.3 XML 配置文件列举了所有能够经常改变详细内容。它完成了许多与 hibernate.cfg.xml 所作的相同的事情,也为我们构建了 SessionFactory ,这一点将在下一节看到。

 

构建 AppicationContext

    你刚刚创建的 applicationContext.xml 详细的描述了怎样构建 session 工厂。它基本上可以一对一的替换你所看到的 HibernateFactory 使用的 hibernate.cfg.xml 。它定义了通常在 hibernate.cfg.xml 中的属性和映射文件。在我们先前的示例代码中,你需要构建 SessionFactory ,或者通过你的 EventDao 对象连接 SessionFactory Spring 颠覆了这个概念。 Spring 为你构建了 EventDao ,你需要询问 EventDao 的首选项,就像:

---------------------------------------------------------------------------------------------

import org.springframework.context.support.ClassPathXmlApplicationContext;
import  com.manning.hq.ch07.Event;

ClassPathXmlApplicationContext ctx 
=   new
ClassPathXmlApplicationContext(
" applicationContext.xml " );
EventSpringDao eventDao 
= (EventSpringDao) ctx.getBean( " eventDao " , EventSpringDao. class );
Event event 
=   new  Event();
eventDao.saveOrUpdate(event);
--------------------------------------------------------------------------------------------

ClasspathXmlApplicationContext 看上去 classpath 中,因为配置文件的名字在 instruction 中已提供( The ClasspathXmlApplicationContext looks in the classpath for the name of the configuration file provided in the instructions. )在这个案例中, applicationContext.xml classpath root (根)。你能够从 application context 通过名字请求 bean getBean ()方法有两个参数: bean 的名字( eventDao ),你期望的类的类型( EventSpringDao )。

在此之下, Spring 构建了 SessionFactory 并将所有的 Bean 连接在一起。我们更早的认识到 Spring Javabean 一起工作。所有的在 applicationContext.xml 文件中的 <bean> 元素都需要 JavaBean 。这包括了象下面的 EventSpringDao

-------------------------------------------------------------------------------------------------------------------------

public   class  EventSpringDao  extends  AbstractSpringDao{
    public  EventSpringDao(){}
    public  Event find(Long id){
        return  (Event)  super .find(Event. class , id);
    }
    //  Other methods excluded
}

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

         另外更早的认识到的好处是, Spring 提供了 org.springframework.orm.hibernate3.support.HibernateDaoSupport , 应用 Dao 层的父型。她管理了 SessionFactory 和一些有用的方法去处理 Session Logging HibernateTemplate 。这有这些方法的示例:

-------------------------------------------------------------------------------

public   abstract   class  HibernateDaoSupport  implements  InitializingBean {
    protected   final  Log logger;
    private  HibernateTemplate hibernateTemplate;
    public   final   void  setSessionFactory(SessionFactory sessionFactory);
    public   final  SessionFactory getSessionFactory();
    public   final   void  setHibernateTemplate(HibernateTemplate hibernateTemplate);
    public   final  HibernateTemplate getHibernateTemplate();                                                protected   final  Session getSession()  throws      DataAccessResourceFailureException, IllegalStateException;
    protected final void closeSessionIfNecessary(Session session);
}

-----------------------------------------------------------------------------------

他提供了一些基本的方法,但我们选择重写 HibernateDaoSupport 对象,为了 提供更多的便利方法。 Listing7.4 显示了改变的类。

Listing 7.4 你的应用的层的父型 DAOs

--------------------------------------------------------------------------------------------

package  com.manning.hq.ch07;

import  java.util.List;
import  org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public   abstract   class  AbstractSpringDao  extends  HibernateDaoSupport{
    public  AbstractSpringDao() { }
    protected   void  saveOrUpdate(Object obj) {
        getHibernateTemplate().saveOrUpdate(obj);
    }

    protected   void  delete(Object obj) {
        getHibernateTemplate().delete(obj);
    }
    protected  Object find(Class clazz, Long id) {
        return  getHibernateTemplate().load(clazz, id);
    }

    protected  List findAll(Class clazz) {
        return  getHibernateTemplate().find( " from  "   +  clazz.getName());
    }
}

------------------------------------------------------------------

重点注意的是 AbstractSpringDao 使用它的父类 sessionFactory 成员。 HibernateDaoSupport 提供了 getter setter 方法, Spring 使用 setter 方法去连接 SessionFactory 。从 applicationContext .xml 文件中调用这些行:

-------------------------------------------------------------------------------

< bean id = " eventDao "   class = " com.manning.hq.ch07.EventSpringDao>
    < property name = " sessionFactory " >
        < ref bean = " factory "   />
    </ property >
</ bean >

------------------------------------------------------------------

这是调用 setSessionFactory () 的片断,通过我们配置的 SessionFactory ,我们叫它为工厂。你所看到的 AbstractSpringDao 是由 AbstractDao 进化而来,你可以从 find() 方法中完整的除去大多数资源管理代码。 HibernateTemplate HibernateDaoSupport 替代了被控制的一切。

 

创建注册( Creating a registry )

我们最终是为了将所有集中到一起并创建中心的注册,开发者能够使用它直接获得参数给 DAO SessionFactory 。通过单一的类, CalendarRegistry ,你能确保将来开发有一个显示的,单一的,强壮类型的类来使用,不需要知道类的详细内部机制。 Figure 7.3 显示了怎样将所有集中到一起。

使用 Spring ,你获得了配置的好处,允许你轻松的交换数据资源,数据库和添加新的对象成员。 Listing7.5 就是 CalendarRegistry

springdao3.JPG

 

Figure 7.3 CalendarRegistry 图表,获得参数到 EventDao

 

Listing 7.5 CalendarRegistry , 组织 Dao 的中心的类

-------------------------------------------------------------------------------------------------------------------------

package  com.manning.hq.ch07;

import  org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import  org.hibernate.SessionFactory;

public   class  CalendarRegistry {
    private   static  ApplicationContext ctx;
    static  {
        ctx 
=   new  ClassPathXmlApplicationContext( " applicationContext.xml " );
    }
    private  CalendarRegistry() {}
    public   static  SessionFactory getSessionFactory() {
        return  (SessionFactory) ctx.getBean( " factory " , SessionFactory. class );
    }
    public   static  EventSpringDao getEventDao() {
        return  (EventSpringDao)ctx.getBean( " eventDao " , EventSpringDao. class );
    }
}

-------------------------------------------------------------------------------------------------------------------------

正如你所见, CalendarRegistry 是单例模式,但是因为它背后是 Spring ,你能够轻松的实现底层的交换。它从 classpath 中装载单一的静态的 ApplicationContext ,然后使用它获取参数。现在客户端对象能够在项目中任何地方获得参数给 EventDao ,不需要知道关于 Spring 做了什么。

-------------------------------------------------------------------------------------------------------------------------

EventSpringDao eventDao  =  CalendarRegistry.getEventDao();
eventDao.saveOrUpdate(event);

--------------------------------------------------------------------------------------------

 

更多的 Spring 工具

就像你所见,使用 Spring 能够最大限度的简化 Hibernate 的资源管理代码。我们显示了两个你能够使用的包含的级别。 HibernateTemplate 能够被嵌入你的 DAO 中,或者使用 Spring 的轻量级容器去管理 DAO

    Spring is a fairly straightforward framework, but we haven’t scratched the surface of what it can do here. Spring 还算一个直接的框架,但我们没有就它能在这做什么过多的纠缠) Spring 也支持事务 API 管理框架, AOP 框架,一个 RuntimeException 框架,能够拦截数据库发出的大多数迟钝的 SQLException ,将它转变成更显而易见和包罗万象的 exception 。更多的信息,请查看实在又全面的 Spring 文档。

 

7.5  总结

这一章的焦点在于提高组织你的应用程序代码。我们集中了 HQL ,使用了 DAO 模式。我们通过加入其它的模式更提高了初始实现,层的父型,它允许增加更多的DAO,不需要复制过多的代码到项目中新添加的对象成员。

    这一章也探索了怎样使用 Spring ,另一个流行的开源项目,管理 Hibernate 需要的 boilerplate (样板)资源管理代码。 Spring 提供了标题选项, HibernateTemplate pluggable ApplicationContext 。增加一个 CalendarRegistry , 它提供了方法使用 Spring 获得项目中的参数,不再需要去包含一个“单块集成电路”了。

 

1 Hibernate 如此优秀,为什么还要用别的?在现实中,转变 ORM 实现并不是微不足道的, DAO 是容易产生漏洞的,提取那样做的话就会有问题,将不能从应用程序中完全隐藏 Hibernate 。( DAOs are leaky enough abstractions that doing so probably won’t completely hide Hibernate from the application. )。 因此不需要放太多的精力去密封 DAO 层,在以后为了可能的某种目的要转变 ORM 实现。

 

2 选自 Patterns of Enterprise Application Architecture , 作者: Martin Fowler(Addision-Wesley 专家, 2003 )

 

3 选自在线文章“ Data Access with Spring Framework ”,作者: Juergen Hoeller 2003 7 月; http://hibernate.bluemars.net/110.html

posted on 2005-12-21 22:21 千山鸟飞绝 阅读(10510) 评论(6)  编辑  收藏 所属分类: Hibernate

FeedBack:
# re: 用spring组织DAO
2005-12-23 15:40 | xmlspy
在这一点上,你将通过组合使用hibernate.cfg.xml文件(declaratively(公告的))和一些活跃的(programmatic)标题,如HibernateFactory来配置Hibernate。Spring提供了另外一个选择去整体declaratively(公告的)配置Hibernate。使用Spring的最大好处就是你能够减少(programmatic)标题配置需要的元素。

-----------------
翻译得很变态 :(

declaratively ----声明性的

一些活跃的(programmatic)---- 使用灵活的编程方式

上面两个是相对应的:
声明性的(指用配置文件) -- 编程方式的、硬编码的(指写java代码)
  回复  更多评论
  
# re: 用spring组织DAO
2005-12-27 22:34 | 千山鸟飞绝
谢谢xmlspy的指正  回复  更多评论
  
# re: 用spring组织DAO
2006-03-11 17:24 | skdk
很通俗易懂的文章,在网上搜索了好久关于Spring DAO 的话题,这篇是我看到最好的, 比较全面的,比较清晰的一篇文章,希望,,在多发,为我们初学Spring 的,提供一个良好的平台,谢谢!!
  回复  更多评论
  
# re: 用spring组织DAO
2007-05-20 19:40 | zhou
楼主写得不错,楼主说的开发模式,我一直用,哈哈,有空大家研究一下  回复  更多评论
  
# re: 用spring组织DAO
2007-08-24 10:15 | chelson
这本书名叫什么? 感觉作者的写作思维相当流畅, 读起来一个字---爽
refactor---------------重构  回复  更多评论
  
# re: 用spring组织DAO
2007-08-24 16:39 | 千山鸟飞绝
@chelson
这本书叫做hibernate quickly,现在有中文版卖,售价也不高。

适合hibernate入门时使用。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
 
正在阅读:



<2007年8月>
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用链接

留言簿(35)

随笔档案

文章分类

文章档案

好友的blog

我的其他blog

老婆的Blog

搜索

  •  

积分与排名

  • 积分 - 773012
  • 排名 - 56

最新评论

阅读排行榜

评论排行榜