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