- 浏览: 697561 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
yzs5273:
没什么用。都试过了
WIN7下CS不能全屏的解决方法 -
di1984HIT:
不错,学习了
读取本地计算机中的安装程序列表 -
ffedu:
[flash=200,200][url][img][list] ...
linux/unix中如何用find命令详解,非常详细的介绍,比man find强100倍(转) -
lintghi:
...
Log4j使用相对路径指定log文件及使用总结 -
nick.s.ni:
唉,Java中引用的包没有介绍啊,如果数据库用UTF-8的格式 ...
Oracle 中Java 对象与PL/SQL类型的映射及使用(转)
使用 Hibernate 和 Spring AOP 构建泛型类型安全的 DAO
简介: 由于 Java™ 5 泛型的采用,有关泛型类型安全 Data Access Object (DAO) 实现的想法变得切实可行。在本文中,系统架构师 Per Mellqvist 展示了基于 Hibernate 的泛型 DAO 实现类。然后展示如何使用 Spring AOP introductions 将类型安全接口添加到类中以便于查询执行。
对于大多数开发人员,为系统中的每个 DAO 编写几乎相同的代码到目前为止已经成为一种习惯。虽然所有人都将这种重复标识为 “代码味道”,但我们大多数都已经学会忍受它。其实有解决方案。可以使用许多 ORM 工具来避免代码重复。例如,使用 Hibernate,您可以简单地为所有的持久域对象直接使用会话操作。这种方法的缺点是损失了类型安全。
为什么您要为数据访问代码提供类型安全接口?我会争辩说,当它与现代 IDE 工具一起使用时,会减少编程错误并提高生产率。首先,类型安全接口清楚地指明哪些域对象具有可用的持久存储。其次,它消除了易出错的类型强制转换的需要(这是一个在查询操作中比在 CRUD 中更常见的问题)。最后,它有效利用了今天大多数 IDE 具备的自动完成特性。使用自动完成是记住什么查询可用于特定域类的快捷方法。
在本文中,我将为您展示如何避免再三地重复 DAO 代码,而仍保留类型安全接口的优点。事实上,您需要为每个新 DAO 编写的只是 Hibernate 映射文件、无格式旧 Java 接口以及 Spring 配置文件中的 10 行。
DAO 模式对任何企业 Java 开发人员来说都应该很熟悉。但是模式的实现各不相同,所以我们来澄清一下本文提供的 DAO 实现背后的假设:
- 系统中的所有数据库访问都通过 DAO 进行以实现封装。
- 每个 DAO 实例负责一个主要域对象或实体。如果域对象具有独立生命周期,它应具有自己的 DAO。
- DAO 负责域对象的创建、读取(按主键)、更新和删除(creations, reads, updates, and deletions,CRUD)。
- DAO 可允许基于除主键之外的标准进行查询。我将之称为查找器方法 或查找器。查找器的返回值通常是 DAO 负责的域对象集合。
- DAO 不负责处理事务、会话或连接。这些不由 DAO 处理是为了实现灵活性。
泛型 DAO 的基础是其 CRUD 操作。下面的接口定义泛型 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 所示。它只需调用底层 Hibernate 方法和添加强制类型转换。Spring 负责会话和事务管理。(当然,我假设这些函数已做了适当的设置,但该主题在 Hibernate 和 Springt 手册中有详细介绍。)
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 配置中,我创建了 GenericDaoHibernateImpl
的一个实例。必须告诉 GenericDaoHibernateImpl
的构造函数 DAO 实例将负责哪个域类。只有这样,Hibernate 才能在运行时知道由 DAO 管理的对象类型。在清单 3 中,我将域类 Person
从示例应用程序传递给构造函数,并将先前配置的 Hibernate 会话工厂设置为已实例化的 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>
我还没有完成,但我所完成的确实已经可以使用了。在清单 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); }
现在,我有一个能够进行类型安全 CRUD 操作的泛型 DAO。让子类 GenericDaoHibernateImpl
为每个域对象添加查询能力将非常合理。因为本文的目的在于展示如何不为每个查询编写显式的 Java 代码来实现查询,但是,我将使用其他两个工具将查询引入 DAO,也就是 Spring AOP 和 Hibernate 命名的查询。
可以使用 Spring AOP 中的 introductions 将功能添加到现有对象,方法是将功能包装在代理中,定义应实现的接口,并将所有先前未支持的方法指派到单个处理程序。在我的 DAO 实现中,我使用 introductions 将许多查找器方法添加到现有泛型 DAO 类中。因为查找器方法是特定于每个域对象的,因此将其应用于泛型 DAO 的类型化接口。
Spring 配置如清单 5 所示:
清单 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 的配置文件中,我定义了三个 Spring bean。第一个 bean 是 FinderIntroductionAdvisor,它处理引入到 DAO 的所有方法,这些方法在 GenericDaoHibernateImpl
类中不可用。我稍后将详细介绍 Advisor bean。
第二个 bean 是 “抽象的”。在 Spring 中,这意味着该 bean 可在其他 bean 定义中被重用,但不被实例化。除了抽象特性之外,该 bean 定义只指出我想要 GenericDaoHibernateImpl
的实例以及该实例需要对 SessionFactory
的引用。注意,GenericDaoHibernateImpl
类仅定义一个构造函数,该构造函数接受域类作为其参数。因为该 bean 定义是抽象的,所以我将来可以无数次地重用该定义,并将构造函数参数设置为合适的域类。
最后,第三个也是最有趣的 bean 将 GenericDaoHibernateImpl
的 vanilla 实例包装在代理中,赋予其执行查找器方法的能力。该 bean 定义也是抽象的,不指定希望引入到 vanilla DAO 的接口。该接口对于每个具体的实例是不同的。注意,清单 5 显示的整个配置仅定义一次。
当然,每个 DAO 的接口都基于 GenericDao
接口。我只需使该接口适应特定的域类并扩展该接口以包括查找器方法。在清单 6 中,可以看到为特定目的扩展的 GenericDao
接口示例:
public interface PersonDao extends GenericDao<Person, Long> { List<Person> findByName(String name); }
很明显,清单 6 中定义的方法旨在按名称查找 Person
。必需的 Java 实现代码全部是泛型代码,在添加更多 DAO 时不需要任何更新。
因为 Spring 配置依赖于先前定义的 “抽象” bean,因此它变得相当简洁。我需要指出 DAO 负责哪个域类,并且需要告诉 Springs 该 DAO 应实现哪个接口(一些方法是直接使用,一些方法则是通过使用 introductions 来使用)。清单 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 版本:
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,可以在 Hibernate 映射文件 (hbm.xml) 中定义 HQL 查询并为其命名。稍后可以通过简单地引用给定名称来在 Java 代码中使用该查询。该方法的优点之一是能够在部署时优化查询,而无需更改代码。您一会将会看到,另一个优点是无需编写任何新 Java 实现代码,就可以实现 “完整的” DAO。清单 9 是带有命名查询的映射文件的示例:
<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 定义了域类 Person
的 Hibernate 映射,该域类具有两个属性:name
和 weight
。Person
是具有上述属性的简单 POJO。该文件还包含一个在数据库中查找 Person
所有实例的查询,其中 “name” 等于提供的参数。Hibernate 不为命名查询提供任何真正的名称空间功能。出于讨论目的,我为所有查询名称都加了域类的短(非限定)名称作为前缀。在现实世界中,使用包括包名称的完全类名可能是更好的主意
您已经看到了为任何域对象创建和配置新 DAO 所必需的全部步骤。三个简单的步骤是:
- 定义一个接口,它扩展
GenericDao
并包含所需的任何查找器方法。 - 将每个查找器的命名查询添加到域对象的 hbm.xml 映射文件。
- 为 DAO 添加 10 行 Spring 配置文件。
查看执行查找器方法的代码(只编写了一次!)来结束我的讨论。
使用的 Spring advisor 和 interceptor 很简单,事实上它们的工作是向后引用 GenericDaoHibernateImplClass
。方法名以 “find” 打头的所有调用都传递给 DAO 和单个方法 executeFinder()
。
清单 10. FinderIntroductionAdvisor 的实现
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); } }
清单 10 的实现中惟一缺少的是 executeFinder()
实现。该代码查看调用的类和方法的名称,并使用配置上的约定将其与 Hibernate 查询的名称相匹配。还可以使用 FinderNamingStrategy
来支持其他命名查询的方法。默认实现查找叫做 “ClassName.methodName
” 的查询,其中 ClassName
是不带包的短名称。清单 11 完成了泛型类型安全 DAO 实现:
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 5 之前,该语言不支持编写既类型安全又 泛型的代码,您必须只能选择其中之一。在本文中,您已经看到一个结合使用 Java 5 泛型与 Spring 和 Hibernate(以及 AOP)等工具来提高生产率的示例。泛型类型安全 DAO 类相当容易编写 —— 您只需要单个接口、一些命名查询和为 Spring 配置添加的 10 行代码 —— 而且可以极大地减少错误并节省时间。
几乎本文的所有代码都是可重用的。尽管您的 DAO 类可能包含此处没有实现的查询和操作类型(比如,批操作),但使用我所展示的技术,您至少应该能够实现其中的一部分。参阅 参考资料 了解其他泛型类型安全 DAO 类实现。
自 Java 语言中出现泛型以来,单个泛型类型安全 DAO 的概念已经成为主题。我曾在 JavaOne 2004 中与 Don Smith 简要讨论了泛型 DAO 的灵活性。本文使用的 DAO 实现类旨在作为示例实现,实际上还存在其他实现。例如,Christian Bauer 已经发布了带有 CRUD 操作和标准搜索的实现,Eric Burke 也在该领域做出了工作。我确信将会有更多的实现出现。我要额外感谢 Christian,他目睹了我编写泛型类型安全 DAO 的第一次尝试并提出改进建议。最后,我要感谢 Ramnivas Laddad 的无价帮助,他审阅了本文。
发表评论
-
Transfer
2017-06-29 23:03 0Find connections count: ... -
Discover the Mystery of Metaspace
2017-06-23 16:47 0The JDK 8 HotSpot JVM is now u ... -
Command Line JMX Client
2014-12-29 13:12 2595Command Line Parser: GNUComman ... -
Something about JVM class loading and initialization
2014-05-09 10:04 1032Class loading stages: Loadin ... -
When a class is loaded and initialized in JVM - Java
2014-05-08 19:09 988from: http://javarevisited.blo ... -
【深入Java虚拟机】之四:类加载机制
2014-05-08 15:12 899转载请注明出处:http://blog.csdn.net/n ... -
Java Reflection - Dynamic Class Loading and Reloading
2014-05-08 12:04 941From: http://tutorials.jenkov. ... -
Java 类加载与初始化
2014-02-19 19:12 808转载自:http://www.cnblogs.c ... -
javax.management.StandardMBean: When and Why. (Reposted)
2013-12-26 15:34 1084Q: When is a Standard MBean no ... -
JVM调优的"标准参数"的各种陷阱(转)
2013-11-11 19:55 2050From: http://hllvm.group.itey ... -
Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
2013-11-11 11:05 1018(From: http://www.oracle.com/ ... -
An article about TLAB
2013-11-11 10:57 756(From: https://blogs.oracle.co ... -
【JVM】HotSpot JVM内存管理和GC策略总结(转)
2013-11-07 23:39 592JVM的相关知识是学习java高级特性必须要去深入学习的。平 ... -
jstat分析VM内存
2013-11-07 16:41 904Jstat 是JDK自带的一个轻量级小工具。全称“Java ... -
java的GridBagLayout网格包布局管理器使用详解 (转)
2013-11-01 16:44 0网格包布局管理是最复 ... -
java动态跟踪分析工具BTrace实现原理
2013-09-01 12:34 1267转自:http://kenwublog.com ... -
Java synchronize用法(转)
2012-11-05 00:20 1207在多个并发线程之间共用资源,就需要进行同步处理。Java虚拟机 ... -
Interview material collection
2012-07-09 23:05 11891. Why can't static methods be ... -
JVM 诊断工具(转)
2011-11-25 12:00 17611.jinfo 描述:输出给定 java 进程所有的配置信 ... -
java中equals和==的区别(转)
2011-11-19 20:07 895值类型是存储在内存中的堆栈(以后简称栈),而引用类型的变 ...
相关推荐
标题“不要重复 DAO!”指的是在软件开发中,尤其是在Java领域,如何避免数据访问对象(DAO)层的代码重复,以提高代码复用性和维护性。DAO模式是将业务逻辑与数据库操作分离的一种设计模式,它使得应用代码可以独立...
### 外文翻译计算机外文翻译“不要重复DAO!”——基于Java 5泛型的类型安全数据访问对象实现 #### 概述 在计算机领域的软件开发过程中,数据访问层(Data Access Layer)的设计与实现至关重要。传统的数据访问...
标题“Don’t repeat the DAO!”指的是在软件开发中避免重复创建数据访问对象(DAO)模式的实践。DAO模式是设计模式的一种,它提供了一种在应用程序代码与数据库交互时解耦的方法。这篇博客可能探讨了如何有效地利用...
当然是用共通的DAO你需要对结果转型,转成你需要的bean,但这也比写那么多DAO强多了,你可以放下包袱,只关注你的业务逻辑。 如果你真能只用一个dao解决,那么祝贺你,你得到了一个虚拟数据层(高度抽象的数据接口)...
"java封转dao层(源码),只需传路径,版本一" 提到的技术是一种简化DAO层创建的方法,通过提供一个通用的接口或抽象类,使得开发者无需为每个实体类编写单独的DAO实现,而是通过传入实体类的路径即可自动创建对应的...
在传统的Hibernate使用中,我们需要为每个实体类编写大量的CRUD(Create, Read, Update, Delete)方法,而使用原生通用DAO可以减少这部分重复工作,提高开发效率。 在设计原生通用DAO时,通常会包含以下核心功能: ...
1. **泛型DAO**:通过泛型,可以创建通用的DAO接口和实现,减少代码的重复。例如,`IMessageDAO` 和 `IUserDAO` 接口定义了基本的CRUD操作,而`HibernateDAO`基础类提供了对这些操作的实现,子类可以进一步扩展以...
这样,我们只需要为每种数据实体创建一个具体的DAO实现类,而无需为每个基本操作重复编写模板代码。 例如,我们可以有如下的泛型DAO接口: ```java public interface GenericDao<T> { void save(T entity); T ...
- **模板方法设计模式**:提供模板类,如`BeanHandler`和`MapHandler`,用于处理结果集,减少重复代码。 - **强大的SQL生成器**:支持动态SQL,可以根据条件自动生成合适的SQL语句。 - **事务管理**:提供事务控制...
总结起来,通过C#的特性标签和反射技术,我们可以构建一个灵活且可扩展的通用Dao层,大大减少了重复代码,提高了开发效率。这种方法的关键在于利用元数据(特性)来描述实体类和数据库之间的关系,以及利用反射在...
对于重复的数据库操作,如增删改查,可以使用模板方法模式来抽象出公共部分,子类只需实现特定的部分。 10. **ORM框架** 虽然JDBC可以满足基本的数据库操作需求,但在大型项目中,通常会选择使用ORM(Object-...
本主题主要探讨的是如何使用泛型和反射技术来实现Hibernate对DAO的封装,从而创建一个通用的DAO模板,减少开发者重复编写CRUD(Create、Read、Update、Delete)操作的工作。 首先,我们需要理解DAO(Data Access ...
在Java编程语言中,泛型和反射是两个非常重要的...这种设计模式在实际开发中非常常见,尤其是在企业级应用中,能够有效地降低代码重复,提高开发效率。同时,通过接口定义,使得不同DAO之间的交互变得更加简单和规范。
这样可以避免代码重复,提高代码的复用性。例如,`AbstractDao`可以包含`executeQuery()`和`executeUpdate()`等通用方法,而`UserDaoImpl`只需实现具体的查询和更新逻辑。 在给定的`TestDao`中,我们可以猜测这是一...
通用DAO的概念旨在减少数据库操作的重复性工作,提高代码的可复用性和可维护性。本文将深入探讨Java语言中如何使用反射机制实现通用DAO,并通过提供的"通用DAO源码及示例"来阐述这一概念。 首先,我们需要理解什么...
这样,你可以快速地为每个实体类创建相应的DAO,而无需手动编写大量重复的代码。 总结来说,通过Eclipse的JET模板和GenenicDao的设计,我们可以有效地自动生成DAO源代码,同时结合Spring和Hibernate,实现高效且...
该插件的主要功能是根据数据库表结构自动生成对应的DAO接口、Mapper XML文件以及实体类,从而避免手动编写这些重复性高且易出错的代码。这不仅减少了开发时间,也保证了代码的一致性和规范性。 使用这个插件之前,...
在本教程中,可能会涉及到提取方法、移动字段、替换条件为策略等重构技术,以消除重复代码、改善类和方法的设计,以及增强代码的表达力。 4. .NET框架:教程可能基于.NET框架进行,这是一个由微软开发的全面的开发...
使用这样的设计,开发者可以快速地为新的实体类型创建DAO,减少了重复的代码编写,并且由于DAO层的解耦,可以更方便地更换底层的持久化技术,比如从Hibernate切换到MyBatis。 总结来说,"dao.rar_dao"提供的是一套...
然而,这种做法可能导致大量重复的模板代码,如打开和关闭Session,事务管理等。 另类实现可以采用动态代理或AOP(面向切面编程)来实现通用DAO。下面是一个使用Java动态代理的例子: 1. 创建一个`BaseDAO`接口,...