??xml version="1.0" encoding="utf-8" standalone="yes"?>国产精品视频一区二区久久,亚洲一区二区三区乱码,97在线精品视频http://www.aygfsteel.com/csuct/zh-cnWed, 18 Jun 2025 20:58:37 GMTWed, 18 Jun 2025 20:58:37 GMT60泛型dao 详细剖析http://www.aygfsteel.com/csuct/archive/2010/03/26/316609.htmlcsuctcsuctThu, 25 Mar 2010 16:13:00 GMThttp://www.aygfsteel.com/csuct/archive/2010/03/26/316609.htmlhttp://www.aygfsteel.com/csuct/comments/316609.htmlhttp://www.aygfsteel.com/csuct/archive/2010/03/26/316609.html#Feedback0http://www.aygfsteel.com/csuct/comments/commentRss/316609.htmlhttp://www.aygfsteel.com/csuct/services/trackbacks/316609.html对于大多数开发h员,为系l中的每?DAO ~写几乎相同的代码到目前为止已经成ؓ一U习惯。虽然所有h都将q种重复标识?“代码味道”Q但我们大多数都已经学会忍受它。其实有解决Ҏ。可以用许?ORM 工具来避免代码重复。例如,使用 HibernateQ您可以单地为所有的持久域对象直接用会话操作。这U方法的~点是损׃cd安全?/p>

Z么您要ؓ数据讉K代码提供cd安全接口Q我会争辩说Q当它与C IDE 工具一起用时Q会减少~程错误q提高生产率。首先,cd安全接口清楚地指明哪些域对象h可用的持久存储。其ơ,它消除了易出错的cd强制转换的需要(q是一个在查询操作中比?CRUD 中更常见的问题)。最后,它有效利用了今天大多?IDE 具备的自动完成特性。用自动完成是C什么查询可用于特定域类的快h法?/p>

在本文中Q我ؓ您展C如何避免再三地重复 DAO 代码Q而仍保留cd安全接口的优炏V事实上Q您需要ؓ每个?DAO ~写的只?Hibernate 映射文g、无格式?Java 接口以及 Spring 配置文g中的 10 行?/p>

DAO 实现

DAO 模式对Q何企?Java 开发h员来说都应该很熟悉。但是模式的实现各不相同Q所以我们来澄清一下本文提供的 DAO 实现背后的假设:

pȝ中的所有数据库讉K都通过 DAO q行以实现封装?
每个 DAO 实例负责一个主要域对象或实体。如果域对象h独立生命周期Q它应具有自q DAO?
DAO 负责域对象的创徏、读取(按主键)、更新和删除Qcreations, reads, updates, and deletionsQCRUDQ?
DAO 可允许基于除主键之外的标准进行查询。我之UCؓ查找器方?或查扑֙。查扑֙的返回值通常?DAO 负责的域对象集合?
DAO 不负责处理事务、会话或q接。这些不?DAO 处理是ؓ了实现灵zL?

 

泛型 DAO 接口

泛型 DAO 的基是其 CRUD 操作。下面的接口定义泛型 DAO 的方法:


清单 1. 泛型 DAO 接口
public interface GenericDao <T, PK extends Serializable> {

    /** Persist the newInstance object into database */
    PK create(T newInstance);

    /** Retrieve an object that was previously persisted to the database using
     *   the indicated id as primary key
     */
    T read(PK id);

    /** Save changes made to a persistent object.  */
    void update(T transientObject);

    /** Remove an object from persistent storage in the database */
    void delete(T persistentObject);
}

 


实现接口

?Hibernate 实现清单 1 中的接口十分单,如清?2 所C。它只需调用底层 Hibernate Ҏ和添加强制类型{换。Spring 负责会话和事务管理。(当然Q我假设q些函数已做了适当的设|,但该主题?Hibernate ?Springt 手册中有详细介绍。)


清单 2. W一个泛?DAO 实现
public class GenericDaoHibernateImpl <T, PK extends Serializable>
    implements GenericDao<T, PK>, FinderExecutor {
    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> type) {
        this.type = type;
    }

    public PK create(T o) {
        return (PK) getSession().save(o);
    }

    public T read(PK id) {
        return (T) getSession().get(type, id);
    }

    public void update(T o) {
        getSession().update(o);
    }

    public void delete(T o) {
        getSession().delete(o);
    }

    // Not showing implementations of getSession() and setSessionFactory()
            }
 


Spring 配置

最后,?Spring 配置中,我创Z GenericDaoHibernateImpl 的一个实例。必d?GenericDaoHibernateImpl 的构造函?DAO 实例负责哪个域cR只有这PHibernate 才能在运行时知道?DAO 理的对象类型。在清单 3 中,我将域类 Person 从示例应用程序传递给构造函敎ͼq将先前配置?Hibernate 会话工厂讄为已实例化的 DAO 的参敎ͼ


清单 3. 配置 DAO
<bean id="personDao" class="genericdao.impl.GenericDaoHibernateImpl">
        <constructor-arg>
            <value>genericdaotest.domain.Person</value>
        </constructor-arg>
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
</bean>
        

 


可用的泛?DAO

我还没有完成Q但我所完成的确实已l可以用了。在清单 4 中,可以看到原封不动使用该泛?DAO 的示例:


清单 4. 使用 DAO
public void someMethodCreatingAPerson() {
    ...
    GenericDao dao = (GenericDao)
     beanFactory.getBean("personDao"); // This should normally be injected

    Person p = new Person("Per", 90);
    dao.create(p);
}
        


现在Q我有一个能够进行类型安?CRUD 操作的泛?DAO。让子类 GenericDaoHibernateImpl 为每个域对象d查询能力非常合理。因为本文的目的在于展示如何不ؓ每个查询~写昑ּ?Java 代码来实现查询,但是Q我用其他两个工具将查询引入 DAOQ也是 Spring AOP ?Hibernate 命名的查询?/p>

 


Spring AOP introductions

可以使用 Spring AOP 中的 introductions 功能添加到现有对象Q方法是功能包装在代理中,定义应实现的接口Qƈ所有先前未支持的方法指zֈ单个处理E序。在我的 DAO 实现中,我?introductions 许多查扑֙Ҏd到现有泛?DAO cM。因为查扑֙Ҏ是特定于每个域对象的Q因此将其应用于泛型 DAO 的类型化接口?/p>

Spring 配置如清?5 所C:


清单 5. FinderIntroductionAdvisor ?Spring 配置
<bean id="finderIntroductionAdvisor" class="genericdao.impl.FinderIntroductionAdvisor"/>

<bean id="abstractDaoTarget"
        class="genericdao.impl.GenericDaoHibernateImpl" abstract="true">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
</bean>

<bean id="abstractDao"
        class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
        <property name="interceptorNames">
            <list>
                <value>finderIntroductionAdvisor</value>
            </list>
        </property>
</bean>
        


在清?5 的配|文件中Q我定义了三?Spring bean。第一?bean ?FinderIntroductionAdvisorQ它处理引入?DAO 的所有方法,q些Ҏ?GenericDaoHibernateImpl cM不可用。我E后详l介l?Advisor bean?/p>

W二?bean ?“抽象?#8221;。在 Spring 中,q意味着?bean 可在其他 bean 定义中被重用Q但不被实例化。除了抽象特性之外,?bean 定义只指出我惌 GenericDaoHibernateImpl 的实例以及该实例需要对 SessionFactory 的引用。注意,GenericDaoHibernateImpl cM定义一个构造函敎ͼ该构造函数接受域cM为其参数。因 bean 定义是抽象的Q所以我来可以无数ơ地重用该定义,q将构造函数参数设|ؓ合适的域类?/p>

最后,W三个也是最有趣?bean ?GenericDaoHibernateImpl ?vanilla 实例包装在代理中Q赋予其执行查找器方法的能力。该 bean 定义也是抽象的,不指定希望引入到 vanilla DAO 的接口。该接口对于每个具体的实例是不同的。注意,清单 5 昄的整个配|仅定义一ơ?/p>

 

扩展 GenericDAO

当然Q每?DAO 的接口都Z GenericDao 接口。我只需使该接口适应特定的域cdƈ扩展该接口以包括查找器方法。在清单 6 中,可以看到为特定目的扩展的 GenericDao 接口CZQ?/p>


清单 6. PersonDao 接口
public interface PersonDao extends GenericDao<Person, Long> {
    List<Person> findByName(String name);
}

 


很明显,清单 6 中定义的Ҏ旨在按名U查?Person。必需?Java 实现代码全部是泛型代码,在添加更?DAO 时不需要Q何更新?/p>

配置 PersonDao

因ؓ Spring 配置依赖于先前定义的 “抽象” beanQ因此它变得相当z。我需要指?DAO 负责哪个域类Qƈ且需要告?Springs ?DAO 应实现哪个接口(一些方法是直接使用Q一些方法则是通过使用 introductions 来用)。清?7 展示?PersonDAO ?Spring 配置文gQ?/p>


清单 7. PersonDao ?Spring 配置
<bean id="personDao" parent="abstractDao">
    <property name="proxyInterfaces">
        <value>genericdaotest.dao.PersonDao</value>
    </property>
    <property name="target">
        <bean parent="abstractDaoTarget">
            <constructor-arg>
                <value>genericdaotest.domain.Person</value>
            </constructor-arg>
        </bean>
    </property>
</bean>
        


在清?8 中,可以看到使用了这个更新后?DAO 版本Q?/p>


清单 8. 使用cd安全接口
public void someMethodCreatingAPerson() {
    ...
    PersonDao dao = (PersonDao)
     beanFactory.getBean("personDao"); // This should normally be injected

    Person p = new Person("Per", 90);
    dao.create(p);

    List<Person> result = dao.findByName("Per"); // Runtime exception
}
        


虽然清单 8 中的代码是用类型安?PersonDao 接口的正方法,?DAO 的实现ƈ不完整。调?findByName() 会导致运行时异常。问题在于我q没有实现调?findByName() 所必需的查询。剩下要做的是指定查询。ؓ更正该问题,我用了 Hibernate 命名查询?/p>

 

Hibernate 命名查询

使用 HibernateQ可以在 Hibernate 映射文g (hbm.xml) 中定?HQL 查询qؓ其命名。稍后可以通过单地引用l定名称来在 Java 代码中用该查询。该Ҏ的优点之一是能够在部v时优化查询,而无需更改代码。您一会将会看刎ͼ另一个优Ҏ无需~写M?Java 实现代码Q就可以实现 “完整?#8221; DAO。清?9 是带有命名查询的映射文g的示例:


清单 9. 带有命名查询的映文?br />  <hibernate-mapping package="genericdaotest.domain">
     <class name="Person">
         <id name="id">
             <generator class="native"/>
         </id>
         <property name="name" />
         <property name="weight" />
     </class>

     <query name="Person.findByName">
         <![CDATA[select p from Person p where p.name = ? ]]>
     </query>
 </hibernate-mapping>
        


清单 9 定义了域c?Person ?Hibernate 映射Q该域类h两个属性:name ?weight。Person 是具有上q属性的?POJO。该文gq包含一个在数据库中查找 Person 所有实例的查询Q其?“name” {于提供的参数。Hibernate 不ؓ命名查询提供M真正的名U空间功能。出于讨论目的,我ؓ所有查询名U都加了域类的短Q非限定Q名UC为前~。在现实世界中,使用包括包名U的完全cd可能是更好的L?/p>


逐步概述

您已l看CZQ何域对象创徏和配|新 DAO 所必需的全部步骤。三个简单的步骤是:

定义一个接口,它扩?GenericDao q包含所需的Q何查扑֙Ҏ?
每个查扑֙的命名查询添加到域对象的 hbm.xml 映射文g?
?DAO d 10 ?Spring 配置文g?
查看执行查找器方法的代码Q只~写了一ơ!Q来l束我的讨论?/p>


可重用的 DAO c?/p>

使用?Spring advisor ?interceptor 很简单,事实上它们的工作是向后引?GenericDaoHibernateImplClass。方法名?“find” 打头的所有调用都传递给 DAO 和单个方?executeFinder()?/p>


清单 10. FinderIntroductionAdvisor 的实?br /> public class FinderIntroductionAdvisor extends DefaultIntroductionAdvisor {
    public FinderIntroductionAdvisor() {
        super(new FinderIntroductionInterceptor());
    }
}

public class FinderIntroductionInterceptor implements IntroductionInterceptor {

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        FinderExecutor genericDao = (FinderExecutor) methodInvocation.getThis();

        String methodName = methodInvocation.getMethod().getName();
        if (methodName.startsWith("find")) {
            Object[] arguments = methodInvocation.getArguments();
            return genericDao.executeFinder(methodInvocation.getMethod(), arguments);
        } else {
            return methodInvocation.proceed();
        }
    }

    public boolean implementsInterface(Class intf) {
        return intf.isInterface() && FinderExecutor.class.isAssignableFrom(intf);
    }
}
 


executeFinder() Ҏ

清单 10 的实C惟一~少的是 executeFinder() 实现。该代码查看调用的类和方法的名称Qƈ使用配置上的U定其?Hibernate 查询的名U相匚w。还可以使用 FinderNamingStrategy 来支持其他命名查询的Ҏ。默认实现查扑֏?“ClassName.methodName” 的查询,其中 ClassName 是不带包的短名称。清?11 完成了泛型类型安?DAO 实现Q?


清单 11. executeFinder() 的实?br /> public List<T> executeFinder(Method method, final Object[] queryArgs) {
     final String queryName = queryNameFromMethod(method);
     final Query namedQuery = getSession().getNamedQuery(queryName);
     String[] namedParameters = namedQuery.getNamedParameters();
     for(int i = 0; i < queryArgs.length; i++) {
             Object arg = queryArgs[i];
             Type argType =  namedQuery.setParameter(i, arg);
      }
      return (List<T>) namedQuery.list();
 }

 public String queryNameFromMethod(Method finderMethod) {
     return type.getSimpleName() + "." + finderMethod.getName();
 }
 

 

l束?/p>

?Java 5 之前Q该语言不支持编写既cd安全?泛型的代码,您必d能选择其中之一。在本文中,您已l看C个结合?Java 5 泛型?Spring ?HibernateQ以?AOPQ等工具来提高生产率的示例。泛型类型安?DAO cȝ当容易编?—?您只需要单个接口、一些命名查询和?Spring 配置d?10 行代?—?而且可以极大地减错误ƈ节省旉?/p>

几乎本文的所有代码都是可重用的。尽您?DAO cd能包含此处没有实现的查询和操作类型(比如Q批操作Q,但用我所展示的技术,您至应该能够实现其中的一部分。参?参考资?了解其他泛型cd安全 DAO cd现?/p>

 

本文来自CSDN博客Qhttp://blog.csdn.net/jhaij/archive/2007/06/17/1655535.aspx



csuct 2010-03-26 00:13 发表评论
]]>
վ֩ģ壺 | | ̨| ͬ| ̨| | ֲ| ˮ| | | | 㶫ʡ| ɽ| ն| | ͼ| | ذ| | ˳| | ͭ| ɳ| ʡ| | ƾ| | | | | ƽ| | | | | ˳| | | ֣| Ϫ| ɽ|