首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

不用重复 DAO!(转)

2012-09-15 
不要重复 DAO!(转)?public interface GenericDao T, PK extends Serializable {/** Persist the newInst

不要重复 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);}

?

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() }?<bean id="personDao" name="code">public void someMethodCreatingAPerson() { ... GenericDao dao = (GenericDao) beanFactory.getBean("personDao"); // This should normally be injected Person p = new Person("Per", 90); dao.create(p);}

?

?

现在,我有一个能够进行类型安全 CRUD 操作的泛型 DAO。让子类?GenericDaoHibernateImpl?为每个域对象添加查询能力将非常合理。因为本文的目的在于展示如何不为每个查询编写显式的 Java 代码来实现查询,但是,我将使用其他两个工具将查询引入 DAO,也就是 Spring AOP 和 Hibernate 命名的查询。

?

?

<bean id="finderIntroductionAdvisor" abstract="true"> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property></bean><bean id="abstractDao" abstract="true"> <property name="interceptorNames"> <list> <value>finderIntroductionAdvisor</value> </list> </property></bean>

?

?

在清单 5 的配置文件中,我定义了三个 Spring bean。第一个 bean 是 FinderIntroductionAdvisor,它处理引入到 DAO 的所有方法,这些方法在?GenericDaoHibernateImpl?类中不可用。我稍后将详细介绍 Advisor bean。

第二个 bean 是 “抽象的”。在 Spring 中,这意味着该 bean 可在其他 bean 定义中被重用,但不被实例化。除了抽象特性之外,该 bean 定义只指出我想要?GenericDaoHibernateImpl?的实例以及该实例需要对?SessionFactory?的引用。注意,GenericDaoHibernateImpl?类仅定义一个构造函数,该构造函数接受域类作为其参数。因为该 bean 定义是抽象的,所以我将来可以无数次地重用该定义,并将构造函数参数设置为合适的域类。

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

?

public interface PersonDao extends GenericDao<Person, Long> { List<Person> findByName(String name);}

??

?

很明显,清单 6 中定义的方法旨在按名称查找?Person。必需的 Java 实现代码全部是泛型代码,在添加更多 DAO 时不需要任何更新。

<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 版本:

?


清单 8. 使用类型安全接口

?

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()?会导致运行时异常。问题在于我还没有实现调用?findByName()?所必需的查询。剩下要做的就是指定查询。为更正该问题,我使用了 Hibernate 命名查询。

?

?

<hibernate-mapping package="genericdaotest.domain"> <class name="Person"> <id name="id"> <generator /> <property name="weight" /> </class> <query name="Person.findByName"> <![CDATA[select p from Person p where p.name = ? ]]> </query> </hibernate-mapping>

?

清单 9 定义了域类?Person?的 Hibernate 映射,该域类具有两个属性:name?和?weightPerson?是具有上述属性的简单 POJO。该文件还包含一个在数据库中查找?Person?所有实例的查询,其中 “name” 等于提供的参数。Hibernate 不为命名查询提供任何真正的名称空间功能。出于讨论目的,我为所有查询名称都加了域类的短(非限定)名称作为前缀。在现实世界中,使用包括包名称的完全类名可能是更好的主意

?

?

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); }}

?

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(); }

??

?

致谢

自 Java 语言中出现泛型以来,单个泛型类型安全 DAO 的概念已经成为主题。我曾在 JavaOne 2004 中与 Don Smith 简要讨论了泛型 DAO 的灵活性。本文使用的 DAO 实现类旨在作为示例实现,实际上还存在其他实现。例如,Christian Bauer 已经发布了带有 CRUD 操作和标准搜索的实现,Eric Burke 也在该领域做出了工作。我确信将会有更多的实现出现。我要额外感谢 Christian,他目睹了我编写泛型类型安全 DAO 的第一次尝试并提出改进建议。最后,我要感谢 Ramnivas Laddad 的无价帮助,他审阅了本文。

?

?

热点排行